/* * Copyright © 2014 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.data2.transaction.queue.inmemory; import co.cask.cdap.common.queue.QueueName; import co.cask.cdap.common.utils.ImmutablePair; import co.cask.cdap.data2.queue.ConsumerConfig; import co.cask.cdap.data2.queue.DequeueResult; import co.cask.cdap.data2.queue.DequeueStrategy; import co.cask.cdap.data2.queue.QueueConsumer; import co.cask.tephra.Transaction; import co.cask.tephra.TransactionAware; import com.google.common.collect.ImmutableList; import java.io.IOException; import java.util.Collection; import java.util.Iterator; import java.util.List; /** * Consumer for an in-memory queue. */ public class InMemoryQueueConsumer implements QueueConsumer, TransactionAware { private static final DequeueResult<byte[]> EMPTY_RESULT = DequeueResult.Empty.result(); private final QueueName queueName; private Transaction currentTx; private boolean committed = false; private final ConsumerConfig config; private final int numGroups; private List<InMemoryQueue.Key> dequeuedKeys; private final InMemoryQueue.ConsumerState state = new InMemoryQueue.ConsumerState(); private final InMemoryQueueService queueService; public InMemoryQueueConsumer(QueueName queueName, ConsumerConfig config, int numGroups, InMemoryQueueService queueService) { this.queueName = queueName; this.queueService = queueService; this.config = config; this.numGroups = numGroups; } private InMemoryQueue getQueue() { return queueService.getQueue(queueName); } @Override public String getTransactionAwareName() { return getClass().getSimpleName() + "(queue = " + queueName + ")"; } @Override public QueueName getQueueName() { return queueName; } @Override public ConsumerConfig getConfig() { return config; } @Override public DequeueResult dequeue() throws IOException { return dequeue(1); } @Override public DequeueResult dequeue(int maxBatchSize) throws IOException { ImmutablePair<List<InMemoryQueue.Key>, List<byte[]>> result = getQueue().dequeue(currentTx, config, state, maxBatchSize); if (result == null) { return EMPTY_RESULT; } else { dequeuedKeys = result.getFirst(); return new InMemoryDequeueResult(result); } } @Override public void startTx(Transaction tx) { currentTx = tx; dequeuedKeys = null; committed = false; } @Override public void updateTx(Transaction transaction) { currentTx = transaction; } @Override public Collection<byte[]> getTxChanges() { return ImmutableList.of(); // no conflicts generated by queues } @Override public boolean commitTx() throws Exception { getQueue().ack(dequeuedKeys, config); committed = true; return true; } @Override public void postTxCommit() { getQueue().evict(dequeuedKeys, numGroups); } @Override public boolean rollbackTx() throws Exception { if (committed || DequeueStrategy.FIFO.equals(config.getDequeueStrategy())) { if (dequeuedKeys != null) { getQueue().undoDequeue(dequeuedKeys, config); } } dequeuedKeys = null; return true; } @Override public void close() throws IOException { // No-op } private final class InMemoryDequeueResult implements DequeueResult<byte[]> { private final List<InMemoryQueue.Key> keys; private final List<byte[]> data; InMemoryDequeueResult(ImmutablePair<List<InMemoryQueue.Key>, List<byte[]>> result) { this.keys = ImmutableList.copyOf(result.getFirst()); this.data = ImmutableList.copyOf(result.getSecond()); } @Override public boolean isEmpty() { return data.isEmpty(); } @Override public void reclaim() { if (dequeuedKeys != null) { dequeuedKeys.addAll(keys); } else { dequeuedKeys = keys; } } @Override public int size() { return keys.size(); } @Override public Iterator<byte[]> iterator() { return data.iterator(); } } }