Search in sources :

Example 1 with QueueConsumer

use of co.cask.cdap.data2.queue.QueueConsumer in project cdap by caskdata.

the class HBaseQueueClientFactory method createConsumer.

@Override
public QueueConsumer createConsumer(final QueueName queueName, final ConsumerConfig consumerConfig, int numGroups) throws IOException {
    final HBaseQueueAdmin admin = ensureTableExists(queueName);
    try {
        final long groupId = consumerConfig.getGroupId();
        // A callback for create a list of HBaseQueueConsumer
        // based on the current queue consumer state of the given group
        Callable<List<HBaseQueueConsumer>> consumerCreator = new Callable<List<HBaseQueueConsumer>>() {

            @Override
            public List<HBaseQueueConsumer> call() throws Exception {
                List<HBaseConsumerState> states;
                try (HBaseConsumerStateStore stateStore = admin.getConsumerStateStore(queueName)) {
                    TransactionExecutor txExecutor = Transactions.createTransactionExecutor(txExecutorFactory, stateStore);
                    // Find all consumer states for consumers that need to be created based on current state
                    states = txExecutor.execute(new Callable<List<HBaseConsumerState>>() {

                        @Override
                        public List<HBaseConsumerState> call() throws Exception {
                            List<HBaseConsumerState> consumerStates = Lists.newArrayList();
                            HBaseConsumerState state = stateStore.getState(groupId, consumerConfig.getInstanceId());
                            if (state.getPreviousBarrier() == null) {
                                // Old HBase consumer (Salted based, not sharded)
                                consumerStates.add(state);
                                return consumerStates;
                            }
                            // Find the smallest start barrier that has something to consume for this instance.
                            // It should always exists since we assume the queue is configured before this method is called
                            List<QueueBarrier> queueBarriers = stateStore.getAllBarriers(groupId);
                            if (queueBarriers.isEmpty()) {
                                throw new IllegalStateException(String.format("No consumer information available. Queue: %s, GroupId: %d, InstanceId: %d", queueName, groupId, consumerConfig.getInstanceId()));
                            }
                            QueueBarrier startBarrier = Iterables.find(Lists.reverse(queueBarriers), new Predicate<QueueBarrier>() {

                                @Override
                                public boolean apply(QueueBarrier barrier) {
                                    return barrier.getGroupConfig().getGroupSize() > consumerConfig.getInstanceId() && stateStore.isAllConsumed(consumerConfig, barrier.getStartRow());
                                }
                            }, queueBarriers.get(0));
                            int groupSize = startBarrier.getGroupConfig().getGroupSize();
                            for (int i = consumerConfig.getInstanceId(); i < groupSize; i += consumerConfig.getGroupSize()) {
                                consumerStates.add(stateStore.getState(groupId, i));
                            }
                            return consumerStates;
                        }
                    });
                }
                List<HBaseQueueConsumer> consumers = Lists.newArrayList();
                for (HBaseConsumerState state : states) {
                    QueueType queueType = (state.getPreviousBarrier() == null) ? QueueType.QUEUE : QueueType.SHARDED_QUEUE;
                    HTable hTable = createHTable(admin.getDataTableId(queueName, queueType));
                    int distributorBuckets = getDistributorBuckets(hTable.getTableDescriptor());
                    HBaseQueueStrategy strategy = (state.getPreviousBarrier() == null) ? new SaltedHBaseQueueStrategy(hBaseTableUtil, distributorBuckets) : new ShardedHBaseQueueStrategy(hBaseTableUtil, distributorBuckets);
                    consumers.add(queueUtil.getQueueConsumer(cConf, hTable, queueName, state, admin.getConsumerStateStore(queueName), strategy));
                }
                return consumers;
            }
        };
        return new SmartQueueConsumer(queueName, consumerConfig, consumerCreator);
    } catch (Exception e) {
        // If there is exception, nothing much can be done here besides propagating
        Throwables.propagateIfPossible(e);
        throw new IOException(e);
    }
}
Also used : HTable(org.apache.hadoop.hbase.client.HTable) Callable(java.util.concurrent.Callable) QueueType(co.cask.cdap.data2.transaction.queue.QueueConstants.QueueType) List(java.util.List) TransactionExecutor(org.apache.tephra.TransactionExecutor) IOException(java.io.IOException) IOException(java.io.IOException)

Example 2 with QueueConsumer

use of co.cask.cdap.data2.queue.QueueConsumer in project cdap by caskdata.

the class LevelDBQueueClientFactory method createConsumer.

@Override
public QueueConsumer createConsumer(QueueName queueName, ConsumerConfig consumerConfig, int numGroups) throws IOException {
    LevelDBQueueAdmin admin = ensureTableExists(queueName);
    LevelDBTableCore core = new LevelDBTableCore(admin.getActualTableName(queueName), service);
    // only the first consumer of each group runs eviction; and only if the number of consumers is known (> 0).
    QueueEvictor evictor = (numGroups <= 0 || consumerConfig.getInstanceId() != 0) ? QueueEvictor.NOOP : createEvictor(queueName, numGroups, core);
    return new LevelDBQueueConsumer(cConf, core, getQueueLock(queueName.toString()), consumerConfig, queueName, evictor);
}
Also used : LevelDBTableCore(co.cask.cdap.data2.dataset2.lib.table.leveldb.LevelDBTableCore) QueueEvictor(co.cask.cdap.data2.transaction.queue.QueueEvictor)

Example 3 with QueueConsumer

use of co.cask.cdap.data2.queue.QueueConsumer in project cdap by caskdata.

the class QueueTest method testQueueAbortRetrySkip.

@Test(timeout = TIMEOUT_MS)
public void testQueueAbortRetrySkip() throws Exception {
    QueueName queueName = QueueName.fromFlowlet(NamespaceId.DEFAULT.getEntityName(), "app", "flow", "flowlet", "queuefailure");
    configureGroups(queueName, ImmutableList.of(new ConsumerGroupConfig(0L, 1, DequeueStrategy.FIFO, null), new ConsumerGroupConfig(1L, 1, DequeueStrategy.HASH, "key")));
    List<ConsumerConfig> consumerConfigs = ImmutableList.of(new ConsumerConfig(0, 0, 1, DequeueStrategy.FIFO, null), new ConsumerConfig(1, 0, 1, DequeueStrategy.HASH, "key"));
    createEnqueueRunnable(queueName, 5, 1, null).run();
    try (QueueConsumer fifoConsumer = queueClientFactory.createConsumer(queueName, consumerConfigs.get(0), 2);
        QueueConsumer hashConsumer = queueClientFactory.createConsumer(queueName, consumerConfigs.get(1), 2)) {
        TransactionContext txContext = createTxContext(fifoConsumer, hashConsumer);
        txContext.start();
        Assert.assertEquals(0, Bytes.toInt(fifoConsumer.dequeue().iterator().next()));
        Assert.assertEquals(0, Bytes.toInt(hashConsumer.dequeue().iterator().next()));
        // Abort the consumer transaction
        txContext.abort();
        // Dequeue again in a new transaction, should see the same entries
        txContext.start();
        Assert.assertEquals(0, Bytes.toInt(fifoConsumer.dequeue().iterator().next()));
        Assert.assertEquals(0, Bytes.toInt(hashConsumer.dequeue().iterator().next()));
        txContext.finish();
        // Dequeue again, now should get next entry
        txContext.start();
        Assert.assertEquals(1, Bytes.toInt(fifoConsumer.dequeue().iterator().next()));
        Assert.assertEquals(1, Bytes.toInt(hashConsumer.dequeue().iterator().next()));
        txContext.finish();
        // Dequeue a result and abort.
        txContext.start();
        DequeueResult<byte[]> fifoResult = fifoConsumer.dequeue();
        DequeueResult<byte[]> hashResult = hashConsumer.dequeue();
        Assert.assertEquals(2, Bytes.toInt(fifoResult.iterator().next()));
        Assert.assertEquals(2, Bytes.toInt(hashResult.iterator().next()));
        txContext.abort();
        // Now skip the result with a new transaction.
        txContext.start();
        fifoResult.reclaim();
        hashResult.reclaim();
        txContext.finish();
        // Dequeue again, it should see a new entry
        txContext.start();
        Assert.assertEquals(3, Bytes.toInt(fifoConsumer.dequeue().iterator().next()));
        Assert.assertEquals(3, Bytes.toInt(hashConsumer.dequeue().iterator().next()));
        txContext.finish();
        // Dequeue again, it should see a new entry
        txContext.start();
        Assert.assertEquals(4, Bytes.toInt(fifoConsumer.dequeue().iterator().next()));
        Assert.assertEquals(4, Bytes.toInt(hashConsumer.dequeue().iterator().next()));
        txContext.finish();
    }
    verifyQueueIsEmpty(queueName, consumerConfigs);
}
Also used : QueueConsumer(co.cask.cdap.data2.queue.QueueConsumer) TransactionContext(org.apache.tephra.TransactionContext) ConsumerConfig(co.cask.cdap.data2.queue.ConsumerConfig) QueueName(co.cask.cdap.common.queue.QueueName) ConsumerGroupConfig(co.cask.cdap.data2.queue.ConsumerGroupConfig) Test(org.junit.Test)

Example 4 with QueueConsumer

use of co.cask.cdap.data2.queue.QueueConsumer in project cdap by caskdata.

the class QueueTest method testRollback.

@Test(timeout = TIMEOUT_MS)
public void testRollback() throws Exception {
    QueueName queueName = QueueName.fromFlowlet(NamespaceId.DEFAULT.getEntityName(), "app", "flow", "flowlet", "queuerollback");
    ConsumerConfig consumerConfig = new ConsumerConfig(0, 0, 1, DequeueStrategy.FIFO, null);
    configureGroups(queueName, ImmutableList.of(consumerConfig));
    try (QueueProducer producer = queueClientFactory.createProducer(queueName);
        QueueConsumer consumer = queueClientFactory.createConsumer(queueName, consumerConfig, 1)) {
        TransactionContext txContext = createTxContext(producer, consumer, new TransactionAware() {

            boolean canCommit = false;

            @Override
            public void startTx(Transaction tx) {
            }

            @Override
            public void updateTx(Transaction tx) {
            }

            @Override
            public Collection<byte[]> getTxChanges() {
                return ImmutableList.of();
            }

            @Override
            public boolean commitTx() throws Exception {
                // Flip-flop between commit success/failure.
                boolean res = canCommit;
                canCommit = !canCommit;
                return res;
            }

            @Override
            public void postTxCommit() {
            }

            @Override
            public boolean rollbackTx() throws Exception {
                return true;
            }

            @Override
            public String getTransactionAwareName() {
                return "test";
            }
        });
        // First, try to enqueue and commit would fail
        txContext.start();
        try {
            producer.enqueue(new QueueEntry(Bytes.toBytes(1)));
            txContext.finish();
            // If reaches here, it's wrong, as exception should be thrown.
            Assert.assertTrue(false);
        } catch (TransactionFailureException e) {
            txContext.abort();
        }
        // Try to enqueue again. Within the same transaction, dequeue should be empty.
        txContext.start();
        producer.enqueue(new QueueEntry(Bytes.toBytes(1)));
        Assert.assertTrue(consumer.dequeue().isEmpty());
        txContext.finish();
        // This time, enqueue has been committed, dequeue would see the item
        txContext.start();
        try {
            Assert.assertEquals(1, Bytes.toInt(consumer.dequeue().iterator().next()));
            txContext.finish();
            // If reaches here, it's wrong, as exception should be thrown.
            Assert.assertTrue(false);
        } catch (TransactionFailureException e) {
            txContext.abort();
        }
        // Dequeue again, since last tx was rollback, this dequeue should see the item again.
        txContext.start();
        Assert.assertEquals(1, Bytes.toInt(consumer.dequeue().iterator().next()));
        txContext.finish();
    }
}
Also used : QueueEntry(co.cask.cdap.data2.queue.QueueEntry) TransactionFailureException(org.apache.tephra.TransactionFailureException) TransactionFailureException(org.apache.tephra.TransactionFailureException) QueueConsumer(co.cask.cdap.data2.queue.QueueConsumer) Transaction(org.apache.tephra.Transaction) QueueProducer(co.cask.cdap.data2.queue.QueueProducer) TransactionContext(org.apache.tephra.TransactionContext) TransactionAware(org.apache.tephra.TransactionAware) Collection(java.util.Collection) ConsumerConfig(co.cask.cdap.data2.queue.ConsumerConfig) QueueName(co.cask.cdap.common.queue.QueueName) Test(org.junit.Test)

Example 5 with QueueConsumer

use of co.cask.cdap.data2.queue.QueueConsumer in project cdap by caskdata.

the class QueueTest method testConcurrentEnqueue.

@Category(SlowTests.class)
@Test
public void testConcurrentEnqueue() throws Exception {
    // This test is for testing multiple producers that writes with a delay after a transaction started.
    // This is for verifying consumer advances the startKey correctly.
    final QueueName queueName = QueueName.fromFlowlet(NamespaceId.DEFAULT.getEntityName(), "app", "flow", "flowlet", "concurrent");
    configureGroups(queueName, ImmutableList.of(new ConsumerGroupConfig(0, 1, DequeueStrategy.FIFO, null)));
    final CyclicBarrier barrier = new CyclicBarrier(4);
    ConsumerConfig consumerConfig = new ConsumerConfig(0, 0, 1, DequeueStrategy.FIFO, null);
    // Starts three producers to enqueue concurrently. For each entry, starts a TX, sleep, enqueue, commit.
    ExecutorService executor = Executors.newFixedThreadPool(3);
    final int entryCount = 50;
    for (int i = 0; i < 3; i++) {
        final QueueProducer producer = queueClientFactory.createProducer(queueName);
        final int producerId = i + 1;
        executor.execute(new Runnable() {

            @Override
            public void run() {
                try {
                    barrier.await();
                    for (int i = 0; i < entryCount; i++) {
                        TransactionContext txContext = createTxContext(producer);
                        txContext.start();
                        // Sleeps at different rate to make the scan in consumer has higher change to see
                        // the transaction but not the entry (as not yet written)
                        TimeUnit.MILLISECONDS.sleep(producerId * 50);
                        producer.enqueue(new QueueEntry(Bytes.toBytes(i)));
                        txContext.finish();
                    }
                } catch (Exception e) {
                    LOG.error(e.getMessage(), e);
                } finally {
                    Closeables.closeQuietly(producer);
                }
            }
        });
    }
    // sum(0..entryCount) * 3
    int expectedSum = entryCount * (entryCount - 1) / 2 * 3;
    try (QueueConsumer consumer = queueClientFactory.createConsumer(queueName, consumerConfig, 1)) {
        // Trigger starts of producer
        barrier.await();
        int dequeueSum = 0;
        int noProgress = 0;
        while (dequeueSum != expectedSum && noProgress < 200) {
            TransactionContext txContext = createTxContext(consumer);
            txContext.start();
            DequeueResult<byte[]> result = consumer.dequeue();
            if (!result.isEmpty()) {
                noProgress = 0;
                int value = Bytes.toInt(result.iterator().next());
                dequeueSum += value;
            } else {
                noProgress++;
                TimeUnit.MILLISECONDS.sleep(10);
            }
            txContext.finish();
        }
        Assert.assertEquals(expectedSum, dequeueSum);
    }
}
Also used : QueueEntry(co.cask.cdap.data2.queue.QueueEntry) TransactionFailureException(org.apache.tephra.TransactionFailureException) CyclicBarrier(java.util.concurrent.CyclicBarrier) QueueConsumer(co.cask.cdap.data2.queue.QueueConsumer) QueueProducer(co.cask.cdap.data2.queue.QueueProducer) TransactionContext(org.apache.tephra.TransactionContext) ExecutorService(java.util.concurrent.ExecutorService) ConsumerConfig(co.cask.cdap.data2.queue.ConsumerConfig) QueueName(co.cask.cdap.common.queue.QueueName) ConsumerGroupConfig(co.cask.cdap.data2.queue.ConsumerGroupConfig) Category(org.junit.experimental.categories.Category) Test(org.junit.Test)

Aggregations

QueueConsumer (co.cask.cdap.data2.queue.QueueConsumer)17 ConsumerConfig (co.cask.cdap.data2.queue.ConsumerConfig)15 QueueName (co.cask.cdap.common.queue.QueueName)12 ConsumerGroupConfig (co.cask.cdap.data2.queue.ConsumerGroupConfig)10 TransactionContext (org.apache.tephra.TransactionContext)9 QueueEntry (co.cask.cdap.data2.queue.QueueEntry)8 Test (org.junit.Test)8 QueueProducer (co.cask.cdap.data2.queue.QueueProducer)7 IOException (java.io.IOException)5 TransactionFailureException (org.apache.tephra.TransactionFailureException)5 DequeueResult (co.cask.cdap.data2.queue.DequeueResult)4 TransactionAware (org.apache.tephra.TransactionAware)4 TransactionExecutor (org.apache.tephra.TransactionExecutor)4 QueueTest (co.cask.cdap.data2.transaction.queue.QueueTest)2 StreamConsumer (co.cask.cdap.data2.transaction.stream.StreamConsumer)2 CyclicBarrier (java.util.concurrent.CyclicBarrier)2 ExecutorService (java.util.concurrent.ExecutorService)2 TableNotFoundException (org.apache.hadoop.hbase.TableNotFoundException)2 TransactionExecutorFactory (org.apache.tephra.TransactionExecutorFactory)2 Tick (co.cask.cdap.api.annotation.Tick)1