co.cask.cdap.runtime.OpenCloseDataSetTest.java Source code

Java tutorial

Introduction

Here is the source code for co.cask.cdap.runtime.OpenCloseDataSetTest.java

Source

/*
 * Copyright  2014-2016 Cask Data, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package co.cask.cdap.runtime;

import co.cask.cdap.DummyAppWithTrackingTable;
import co.cask.cdap.TrackingTable;
import co.cask.cdap.api.flow.flowlet.StreamEvent;
import co.cask.cdap.app.program.Program;
import co.cask.cdap.app.runtime.ProgramController;
import co.cask.cdap.app.runtime.ProgramRunner;
import co.cask.cdap.app.runtime.ProgramRunnerFactory;
import co.cask.cdap.common.app.RunIds;
import co.cask.cdap.common.discovery.RandomEndpointStrategy;
import co.cask.cdap.common.io.Locations;
import co.cask.cdap.common.namespace.NamespacedLocationFactory;
import co.cask.cdap.common.queue.QueueName;
import co.cask.cdap.common.stream.StreamEventCodec;
import co.cask.cdap.data2.queue.QueueClientFactory;
import co.cask.cdap.data2.queue.QueueEntry;
import co.cask.cdap.data2.queue.QueueProducer;
import co.cask.cdap.internal.AppFabricTestHelper;
import co.cask.cdap.internal.DefaultId;
import co.cask.cdap.internal.app.deploy.pipeline.ApplicationWithPrograms;
import co.cask.cdap.internal.app.runtime.BasicArguments;
import co.cask.cdap.internal.app.runtime.ProgramOptionConstants;
import co.cask.cdap.internal.app.runtime.SimpleProgramOptions;
import co.cask.cdap.proto.ProgramType;
import co.cask.cdap.test.XSlowTests;
import co.cask.tephra.Transaction;
import co.cask.tephra.TransactionAware;
import co.cask.tephra.TransactionSystemClient;
import com.google.common.base.Charsets;
import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.twill.discovery.Discoverable;
import org.apache.twill.discovery.DiscoveryServiceClient;
import org.apache.twill.filesystem.Location;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.TemporaryFolder;

import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Tests that flowlets and batch jobs close their data sets.
 */
@Category(XSlowTests.class)
public class OpenCloseDataSetTest {

    @ClassRule
    public static final TemporaryFolder TEMP_FOLDER = new TemporaryFolder();
    private static Location namespaceHomeLocation;

    private static final Supplier<File> TEMP_FOLDER_SUPPLIER = new Supplier<File>() {

        @Override
        public File get() {
            try {
                return TEMP_FOLDER.newFolder();
            } catch (IOException e) {
                throw Throwables.propagate(e);
            }
        }
    };

    @BeforeClass
    public static void setup() throws IOException {
        NamespacedLocationFactory namespacedLocationFactory = AppFabricTestHelper.getInjector()
                .getInstance(NamespacedLocationFactory.class);
        namespaceHomeLocation = namespacedLocationFactory.get(DefaultId.NAMESPACE);
        Locations.mkdirsIfNotExists(namespaceHomeLocation);
    }

    @Test(timeout = 120000)
    public void testDataSetsAreClosed() throws Exception {
        final String tableName = "foo";

        TrackingTable.resetTracker();
        ApplicationWithPrograms app = AppFabricTestHelper
                .deployApplicationWithManager(DummyAppWithTrackingTable.class, TEMP_FOLDER_SUPPLIER);
        ProgramRunnerFactory runnerFactory = AppFabricTestHelper.getInjector()
                .getInstance(ProgramRunnerFactory.class);
        List<ProgramController> controllers = Lists.newArrayList();

        // start the programs
        for (Program program : app.getPrograms()) {
            if (program.getType().equals(ProgramType.MAPREDUCE)) {
                continue;
            }
            ProgramRunner runner = runnerFactory.create(program.getType());
            BasicArguments systemArgs = new BasicArguments(
                    ImmutableMap.of(ProgramOptionConstants.RUN_ID, RunIds.generate().getId()));
            controllers.add(runner.run(program,
                    new SimpleProgramOptions(program.getName(), systemArgs, new BasicArguments())));
        }

        // write some data to queue
        TransactionSystemClient txSystemClient = AppFabricTestHelper.getInjector()
                .getInstance(TransactionSystemClient.class);

        QueueName queueName = QueueName.fromStream(app.getId().getNamespaceId(), "xx");
        QueueClientFactory queueClientFactory = AppFabricTestHelper.getInjector()
                .getInstance(QueueClientFactory.class);
        QueueProducer producer = queueClientFactory.createProducer(queueName);

        // start tx to write in queue in tx
        Transaction tx = txSystemClient.startShort();
        ((TransactionAware) producer).startTx(tx);

        StreamEventCodec codec = new StreamEventCodec();
        for (int i = 0; i < 4; i++) {
            String msg = "x" + i;
            StreamEvent event = new StreamEvent(ImmutableMap.<String, String>of(),
                    ByteBuffer.wrap(msg.getBytes(Charsets.UTF_8)));
            producer.enqueue(new QueueEntry(codec.encodePayload(event)));
        }

        // commit tx
        ((TransactionAware) producer).commitTx();
        txSystemClient.commit(tx);

        while (TrackingTable.getTracker(tableName, "write") < 4) {
            TimeUnit.MILLISECONDS.sleep(50);
        }

        // get the number of writes to the foo table
        Assert.assertEquals(4, TrackingTable.getTracker(tableName, "write"));
        // only 2 "open" calls should be tracked:
        // 1. the flow has started with single flowlet (service is loaded lazily on 1st request)
        // 2. DatasetSystemMetadataWriter also instantiates the dataset because it needs to add some system tags
        // for the dataset
        Assert.assertEquals(2, TrackingTable.getTracker(tableName, "open"));

        // now send a request to the service
        Gson gson = new Gson();
        DiscoveryServiceClient discoveryServiceClient = AppFabricTestHelper.getInjector()
                .getInstance(DiscoveryServiceClient.class);

        Discoverable discoverable = new RandomEndpointStrategy(discoveryServiceClient
                .discover(String.format("service.%s.%s.%s", DefaultId.NAMESPACE.getId(), "dummy", "DummyService")))
                        .pick(5, TimeUnit.SECONDS);
        Assert.assertNotNull(discoverable);

        HttpClient client = new DefaultHttpClient();
        HttpGet get = new HttpGet(String.format("http://%s:%d/v3/namespaces/default/apps/%s/services/%s/methods/%s",
                discoverable.getSocketAddress().getHostName(), discoverable.getSocketAddress().getPort(), "dummy",
                "DummyService", "x1"));
        HttpResponse response = client.execute(get);
        String responseContent = gson
                .fromJson(new InputStreamReader(response.getEntity().getContent(), Charsets.UTF_8), String.class);
        client.getConnectionManager().shutdown();
        Assert.assertEquals("x1", responseContent);

        // now the dataset must have a read and another open operation
        Assert.assertEquals(1, TrackingTable.getTracker(tableName, "read"));
        Assert.assertEquals(3, TrackingTable.getTracker(tableName, "open"));
        // The dataset that was instantiated by the DatasetSystemMetadataWriter should have been closed
        Assert.assertEquals(1, TrackingTable.getTracker(tableName, "close"));

        // stop all programs, they should both close the data set foo
        for (ProgramController controller : controllers) {
            controller.stop().get();
        }
        int timesOpened = TrackingTable.getTracker(tableName, "open");
        Assert.assertTrue(timesOpened >= 2);
        Assert.assertEquals(timesOpened, TrackingTable.getTracker(tableName, "close"));

        // now start the m/r job
        ProgramController controller = null;
        for (Program program : app.getPrograms()) {
            if (program.getType().equals(ProgramType.MAPREDUCE)) {
                ProgramRunner runner = runnerFactory.create(program.getType());
                BasicArguments systemArgs = new BasicArguments(
                        ImmutableMap.of(ProgramOptionConstants.RUN_ID, RunIds.generate().getId()));
                controller = runner.run(program,
                        new SimpleProgramOptions(program.getName(), systemArgs, new BasicArguments()));
            }
        }
        Assert.assertNotNull(controller);

        while (!controller.getState().equals(ProgramController.State.COMPLETED)) {
            TimeUnit.MILLISECONDS.sleep(100);
        }

        // M/r job is done, one mapper and the m/r client should have opened and closed the data set foo
        // we don't know the exact number of times opened, but it is at least once, and it must be closed the same number
        // of times.
        Assert.assertTrue(timesOpened < TrackingTable.getTracker(tableName, "open"));
        Assert.assertEquals(TrackingTable.getTracker(tableName, "open"),
                TrackingTable.getTracker(tableName, "close"));
        Assert.assertTrue(0 < TrackingTable.getTracker("bar", "open"));
        Assert.assertEquals(TrackingTable.getTracker("bar", "open"), TrackingTable.getTracker("bar", "close"));

    }

    @AfterClass
    public static void tearDown() throws IOException {
        Locations.deleteQuietly(namespaceHomeLocation, true);
    }
}