Search in sources :

Example 1 with TransactionConflictException

use of org.apache.pulsar.transaction.common.exception.TransactionConflictException in project pulsar by apache.

the class PersistentSubscriptionTest method testCanAcknowledgeAndAbortForTransaction.

@Test
public void testCanAcknowledgeAndAbortForTransaction() throws Exception {
    List<MutablePair<PositionImpl, Integer>> positionsPair = new ArrayList<>();
    positionsPair.add(new MutablePair<>(new PositionImpl(2, 1), 0));
    positionsPair.add(new MutablePair<>(new PositionImpl(2, 3), 0));
    positionsPair.add(new MutablePair<>(new PositionImpl(2, 5), 0));
    doAnswer((invocationOnMock) -> {
        ((AsyncCallbacks.DeleteCallback) invocationOnMock.getArguments()[1]).deleteComplete(invocationOnMock.getArguments()[2]);
        return null;
    }).when(cursorMock).asyncDelete(any(List.class), any(AsyncCallbacks.DeleteCallback.class), any());
    doReturn(CommandSubscribe.SubType.Exclusive).when(consumerMock).subType();
    Awaitility.await().until(() -> {
        try {
            persistentSubscription.addConsumer(consumerMock);
            return true;
        } catch (Exception e) {
            return false;
        }
    });
    // Single ack for txn1
    persistentSubscription.transactionIndividualAcknowledge(txnID1, positionsPair);
    List<PositionImpl> positions = new ArrayList<>();
    positions.add(new PositionImpl(1, 100));
    // Cumulative ack for txn1
    persistentSubscription.transactionCumulativeAcknowledge(txnID1, positions).get();
    positions.clear();
    positions.add(new PositionImpl(2, 1));
    // Can not single ack message already acked.
    try {
        persistentSubscription.transactionIndividualAcknowledge(txnID2, positionsPair).get();
        fail("Single acknowledge for transaction2 should fail. ");
    } catch (ExecutionException e) {
        assertEquals(e.getCause().getMessage(), "[persistent://prop/use/ns-abc/successTopic][subscriptionName] " + "Transaction:(1,2) try to ack message:2:1 in pending ack status.");
    }
    positions.clear();
    positions.add(new PositionImpl(2, 50));
    // Can not cumulative ack message for another txn.
    try {
        persistentSubscription.transactionCumulativeAcknowledge(txnID2, positions).get();
        fail("Cumulative acknowledge for transaction2 should fail. ");
    } catch (ExecutionException e) {
        assertTrue(e.getCause() instanceof TransactionConflictException);
        assertEquals(e.getCause().getMessage(), "[persistent://prop/use/ns-abc/successTopic]" + "[subscriptionName] Transaction:(1,2) try to cumulative batch ack position: " + "2:50 within range of current currentPosition: 1:100");
    }
    List<Position> positionList = new ArrayList<>();
    positionList.add(new PositionImpl(1, 1));
    positionList.add(new PositionImpl(1, 3));
    positionList.add(new PositionImpl(1, 5));
    positionList.add(new PositionImpl(3, 1));
    positionList.add(new PositionImpl(3, 3));
    positionList.add(new PositionImpl(3, 5));
    // Acknowledge from normal consumer will succeed ignoring message acked by ongoing transaction.
    persistentSubscription.acknowledgeMessage(positionList, AckType.Individual, Collections.emptyMap());
    // Abort txn.
    persistentSubscription.endTxn(txnID1.getMostSigBits(), txnID2.getLeastSigBits(), TxnAction.ABORT_VALUE, -1);
    positions.clear();
    positions.add(new PositionImpl(2, 50));
    // Retry above ack, will succeed. As abort has clear pending_ack for those messages.
    persistentSubscription.transactionCumulativeAcknowledge(txnID2, positions);
    positionsPair.clear();
    positionsPair.add(new MutablePair(new PositionImpl(2, 1), 0));
    persistentSubscription.transactionIndividualAcknowledge(txnID2, positionsPair);
}
Also used : Position(org.apache.bookkeeper.mledger.Position) PositionImpl(org.apache.bookkeeper.mledger.impl.PositionImpl) ArrayList(java.util.ArrayList) TransactionConflictException(org.apache.pulsar.transaction.common.exception.TransactionConflictException) TransactionConflictException(org.apache.pulsar.transaction.common.exception.TransactionConflictException) ExecutionException(java.util.concurrent.ExecutionException) MutablePair(org.apache.commons.lang3.tuple.MutablePair) List(java.util.List) ArrayList(java.util.ArrayList) ExecutionException(java.util.concurrent.ExecutionException) Test(org.testng.annotations.Test)

Example 2 with TransactionConflictException

use of org.apache.pulsar.transaction.common.exception.TransactionConflictException in project pulsar by apache.

the class Consumer method transactionIndividualAcknowledge.

private CompletableFuture<Void> transactionIndividualAcknowledge(long txnidMostBits, long txnidLeastBits, List<MutablePair<PositionImpl, Integer>> positionList) {
    if (subscription instanceof PersistentSubscription) {
        TxnID txnID = new TxnID(txnidMostBits, txnidLeastBits);
        return ((PersistentSubscription) subscription).transactionIndividualAcknowledge(txnID, positionList);
    } else {
        String error = "Transaction acknowledge only support the `PersistentSubscription`.";
        log.error(error);
        return FutureUtil.failedFuture(new TransactionConflictException(error));
    }
}
Also used : TxnID(org.apache.pulsar.client.api.transaction.TxnID) TransactionConflictException(org.apache.pulsar.transaction.common.exception.TransactionConflictException) PersistentSubscription(org.apache.pulsar.broker.service.persistent.PersistentSubscription)

Example 3 with TransactionConflictException

use of org.apache.pulsar.transaction.common.exception.TransactionConflictException in project pulsar by apache.

the class Consumer method transactionCumulativeAcknowledge.

private CompletableFuture<Void> transactionCumulativeAcknowledge(long txnidMostBits, long txnidLeastBits, List<PositionImpl> positionList) {
    if (!isTransactionEnabled()) {
        return FutureUtil.failedFuture(new BrokerServiceException.NotAllowedException("Server don't support transaction ack!"));
    }
    if (subscription instanceof PersistentSubscription) {
        TxnID txnID = new TxnID(txnidMostBits, txnidLeastBits);
        return ((PersistentSubscription) subscription).transactionCumulativeAcknowledge(txnID, positionList);
    } else {
        String error = "Transaction acknowledge only support the `PersistentSubscription`.";
        log.error(error);
        return FutureUtil.failedFuture(new TransactionConflictException(error));
    }
}
Also used : TxnID(org.apache.pulsar.client.api.transaction.TxnID) TransactionConflictException(org.apache.pulsar.transaction.common.exception.TransactionConflictException) PersistentSubscription(org.apache.pulsar.broker.service.persistent.PersistentSubscription)

Example 4 with TransactionConflictException

use of org.apache.pulsar.transaction.common.exception.TransactionConflictException in project pulsar by apache.

the class PendingAckHandleImpl method internalIndividualAcknowledgeMessage.

public void internalIndividualAcknowledgeMessage(TxnID txnID, List<MutablePair<PositionImpl, Integer>> positions, CompletableFuture<Void> completableFuture) {
    if (txnID == null) {
        completableFuture.completeExceptionally(new NotAllowedException("Positions can not be null."));
        return;
    }
    if (positions == null) {
        completableFuture.completeExceptionally(new NotAllowedException("Positions can not be null."));
        return;
    }
    this.pendingAckStoreFuture.thenAccept(pendingAckStore -> pendingAckStore.appendIndividualAck(txnID, positions).thenAccept(v -> {
        synchronized (org.apache.pulsar.broker.transaction.pendingack.impl.PendingAckHandleImpl.this) {
            for (MutablePair<PositionImpl, Integer> positionIntegerMutablePair : positions) {
                if (log.isDebugEnabled()) {
                    log.debug("[{}] individualAcknowledgeMessage position: [{}], " + "txnId: [{}], subName: [{}]", topicName, positionIntegerMutablePair.left, txnID, subName);
                }
                PositionImpl position = positionIntegerMutablePair.left;
                // normal acknowledge,throw exception.
                if (((ManagedCursorImpl) persistentSubscription.getCursor()).isMessageDeleted(position)) {
                    String errorMsg = "[" + topicName + "][" + subName + "] Transaction:" + txnID + " try to ack message:" + position + " already acked before.";
                    log.error(errorMsg);
                    completableFuture.completeExceptionally(new TransactionConflictException(errorMsg));
                    return;
                }
                if (position.hasAckSet()) {
                    // in order to jude the bit set is over lap, so set the covering
                    // the batch size bit to 1,should know the two
                    // bit set don't have the same point is 0
                    BitSetRecyclable bitSetRecyclable = BitSetRecyclable.valueOf(position.getAckSet());
                    if (positionIntegerMutablePair.right > bitSetRecyclable.size()) {
                        bitSetRecyclable.set(positionIntegerMutablePair.right);
                    }
                    bitSetRecyclable.set(positionIntegerMutablePair.right, bitSetRecyclable.size());
                    long[] ackSetOverlap = bitSetRecyclable.toLongArray();
                    bitSetRecyclable.recycle();
                    if (isAckSetOverlap(ackSetOverlap, ((ManagedCursorImpl) persistentSubscription.getCursor()).getBatchPositionAckSet(position))) {
                        String errorMsg = "[" + topicName + "][" + subName + "] Transaction:" + txnID + " try to ack message:" + position + " already acked before.";
                        log.error(errorMsg);
                        completableFuture.completeExceptionally(new TransactionConflictException(errorMsg));
                        return;
                    }
                    if (individualAckPositions != null && individualAckPositions.containsKey(position) && isAckSetOverlap(individualAckPositions.get(position).getLeft().getAckSet(), ackSetOverlap)) {
                        String errorMsg = "[" + topicName + "][" + subName + "] Transaction:" + txnID + " try to ack batch message:" + position + " in pending ack status.";
                        log.error(errorMsg);
                        completableFuture.completeExceptionally(new TransactionConflictException(errorMsg));
                        return;
                    }
                } else {
                    if (individualAckPositions != null && individualAckPositions.containsKey(position)) {
                        String errorMsg = "[" + topicName + "][" + subName + "] Transaction:" + txnID + " try to ack message:" + position + " in pending ack status.";
                        log.error(errorMsg);
                        completableFuture.completeExceptionally(new TransactionConflictException(errorMsg));
                        return;
                    }
                }
            }
            handleIndividualAck(txnID, positions);
            completableFuture.complete(null);
        }
    }).exceptionally(e -> {
        synchronized (PendingAckHandleImpl.this) {
            // we also modify the in memory state when append fail,
            // because we don't know the persistent state, when were replay it,
            // it will produce the wrong operation. so we append fail,
            // we should wait tc time out or client abort this transaction.
            handleIndividualAck(txnID, positions);
            completableFuture.completeExceptionally(e.getCause());
        }
        return null;
    })).exceptionally(e -> {
        completableFuture.completeExceptionally(e);
        return null;
    });
}
Also used : ServiceUnitNotReadyException(org.apache.pulsar.broker.service.BrokerServiceException.ServiceUnitNotReadyException) Getter(lombok.Getter) LinkedMap(org.apache.commons.collections4.map.LinkedMap) NotAllowedException(org.apache.pulsar.broker.service.BrokerServiceException.NotAllowedException) AckType(org.apache.pulsar.common.api.proto.CommandAck.AckType) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) Consumer(org.apache.pulsar.broker.service.Consumer) ArrayList(java.util.ArrayList) TxnID(org.apache.pulsar.client.api.transaction.TxnID) PositionAckSetUtil.isAckSetOverlap(org.apache.bookkeeper.mledger.util.PositionAckSetUtil.isAckSetOverlap) MutablePair(org.apache.commons.lang3.tuple.MutablePair) Pair(org.apache.commons.lang3.tuple.Pair) ManagedLedger(org.apache.bookkeeper.mledger.ManagedLedger) Map(java.util.Map) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) PositionAckSetUtil.compareToWithAckSet(org.apache.bookkeeper.mledger.util.PositionAckSetUtil.compareToWithAckSet) ExecutorService(java.util.concurrent.ExecutorService) PositionImpl(org.apache.bookkeeper.mledger.impl.PositionImpl) DEFAULT_CONSUMER_EPOCH(org.apache.pulsar.common.protocol.Commands.DEFAULT_CONSUMER_EPOCH) TransactionConflictException(org.apache.pulsar.transaction.common.exception.TransactionConflictException) BitSetRecyclable(org.apache.pulsar.common.util.collections.BitSetRecyclable) TransactionPendingAckStoreProvider(org.apache.pulsar.broker.transaction.pendingack.TransactionPendingAckStoreProvider) BlockingQueue(java.util.concurrent.BlockingQueue) Position(org.apache.bookkeeper.mledger.Position) ManagedCursorImpl(org.apache.bookkeeper.mledger.impl.ManagedCursorImpl) List(java.util.List) ConcurrentSkipListMap(java.util.concurrent.ConcurrentSkipListMap) Slf4j(lombok.extern.slf4j.Slf4j) PendingAckHandle(org.apache.pulsar.broker.transaction.pendingack.PendingAckHandle) FutureUtil(org.apache.pulsar.common.util.FutureUtil) PositionAckSetUtil.andAckSet(org.apache.bookkeeper.mledger.util.PositionAckSetUtil.andAckSet) TransactionPendingAckStats(org.apache.pulsar.common.policies.data.TransactionPendingAckStats) PersistentSubscription(org.apache.pulsar.broker.service.persistent.PersistentSubscription) LinkedBlockingDeque(java.util.concurrent.LinkedBlockingDeque) PendingAckStore(org.apache.pulsar.broker.transaction.pendingack.PendingAckStore) Collections(java.util.Collections) TransactionInPendingAckStats(org.apache.pulsar.common.policies.data.TransactionInPendingAckStats) MutablePair(org.apache.commons.lang3.tuple.MutablePair) ManagedCursorImpl(org.apache.bookkeeper.mledger.impl.ManagedCursorImpl) NotAllowedException(org.apache.pulsar.broker.service.BrokerServiceException.NotAllowedException) BitSetRecyclable(org.apache.pulsar.common.util.collections.BitSetRecyclable) PositionImpl(org.apache.bookkeeper.mledger.impl.PositionImpl) TransactionConflictException(org.apache.pulsar.transaction.common.exception.TransactionConflictException)

Example 5 with TransactionConflictException

use of org.apache.pulsar.transaction.common.exception.TransactionConflictException in project pulsar by apache.

the class PendingAckHandleImpl method internalCumulativeAcknowledgeMessage.

public void internalCumulativeAcknowledgeMessage(TxnID txnID, List<PositionImpl> positions, CompletableFuture<Void> completableFuture) {
    if (txnID == null) {
        completableFuture.completeExceptionally(new NotAllowedException("TransactionID can not be null."));
        return;
    }
    if (positions == null) {
        completableFuture.completeExceptionally(new NotAllowedException("Positions can not be null."));
        return;
    }
    if (positions.size() != 1) {
        String errorMsg = "[" + topicName + "][" + subName + "] Transaction:" + txnID + " invalid cumulative ack received with multiple message ids.";
        log.error(errorMsg);
        completableFuture.completeExceptionally(new NotAllowedException(errorMsg));
        return;
    }
    PositionImpl position = positions.get(0);
    this.pendingAckStoreFuture.thenAccept(pendingAckStore -> pendingAckStore.appendCumulativeAck(txnID, position).thenAccept(v -> {
        if (log.isDebugEnabled()) {
            log.debug("[{}] cumulativeAcknowledgeMessage position: [{}], " + "txnID:[{}], subName: [{}].", topicName, txnID.toString(), position, subName);
        }
        if (position.compareTo((PositionImpl) persistentSubscription.getCursor().getMarkDeletedPosition()) <= 0) {
            String errorMsg = "[" + topicName + "][" + subName + "] Transaction:" + txnID + " try to cumulative ack position: " + position + " within range of cursor's " + "markDeletePosition: " + persistentSubscription.getCursor().getMarkDeletedPosition();
            log.error(errorMsg);
            completableFuture.completeExceptionally(new TransactionConflictException(errorMsg));
            return;
        }
        if (cumulativeAckOfTransaction != null && (!cumulativeAckOfTransaction.getKey().equals(txnID) || compareToWithAckSet(position, cumulativeAckOfTransaction.getValue()) <= 0)) {
            String errorMsg = "[" + topicName + "][" + subName + "] Transaction:" + txnID + " try to cumulative batch ack position: " + position + " within range of current " + "currentPosition: " + cumulativeAckOfTransaction.getValue();
            log.error(errorMsg);
            completableFuture.completeExceptionally(new TransactionConflictException(errorMsg));
            return;
        }
        handleCumulativeAck(txnID, position);
        completableFuture.complete(null);
    }).exceptionally(e -> {
        // we also modify the in memory state when append fail, because we don't know the persistent
        // state, when wereplay it, it will produce the wrong operation. so we append fail, we should
        // wait tc time out or client abort this transaction.
        handleCumulativeAck(txnID, position);
        completableFuture.completeExceptionally(e.getCause());
        return null;
    })).exceptionally(e -> {
        completableFuture.completeExceptionally(e);
        return null;
    });
}
Also used : ServiceUnitNotReadyException(org.apache.pulsar.broker.service.BrokerServiceException.ServiceUnitNotReadyException) Getter(lombok.Getter) LinkedMap(org.apache.commons.collections4.map.LinkedMap) NotAllowedException(org.apache.pulsar.broker.service.BrokerServiceException.NotAllowedException) AckType(org.apache.pulsar.common.api.proto.CommandAck.AckType) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) Consumer(org.apache.pulsar.broker.service.Consumer) ArrayList(java.util.ArrayList) TxnID(org.apache.pulsar.client.api.transaction.TxnID) PositionAckSetUtil.isAckSetOverlap(org.apache.bookkeeper.mledger.util.PositionAckSetUtil.isAckSetOverlap) MutablePair(org.apache.commons.lang3.tuple.MutablePair) Pair(org.apache.commons.lang3.tuple.Pair) ManagedLedger(org.apache.bookkeeper.mledger.ManagedLedger) Map(java.util.Map) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) PositionAckSetUtil.compareToWithAckSet(org.apache.bookkeeper.mledger.util.PositionAckSetUtil.compareToWithAckSet) ExecutorService(java.util.concurrent.ExecutorService) PositionImpl(org.apache.bookkeeper.mledger.impl.PositionImpl) DEFAULT_CONSUMER_EPOCH(org.apache.pulsar.common.protocol.Commands.DEFAULT_CONSUMER_EPOCH) TransactionConflictException(org.apache.pulsar.transaction.common.exception.TransactionConflictException) BitSetRecyclable(org.apache.pulsar.common.util.collections.BitSetRecyclable) TransactionPendingAckStoreProvider(org.apache.pulsar.broker.transaction.pendingack.TransactionPendingAckStoreProvider) BlockingQueue(java.util.concurrent.BlockingQueue) Position(org.apache.bookkeeper.mledger.Position) ManagedCursorImpl(org.apache.bookkeeper.mledger.impl.ManagedCursorImpl) List(java.util.List) ConcurrentSkipListMap(java.util.concurrent.ConcurrentSkipListMap) Slf4j(lombok.extern.slf4j.Slf4j) PendingAckHandle(org.apache.pulsar.broker.transaction.pendingack.PendingAckHandle) FutureUtil(org.apache.pulsar.common.util.FutureUtil) PositionAckSetUtil.andAckSet(org.apache.bookkeeper.mledger.util.PositionAckSetUtil.andAckSet) TransactionPendingAckStats(org.apache.pulsar.common.policies.data.TransactionPendingAckStats) PersistentSubscription(org.apache.pulsar.broker.service.persistent.PersistentSubscription) LinkedBlockingDeque(java.util.concurrent.LinkedBlockingDeque) PendingAckStore(org.apache.pulsar.broker.transaction.pendingack.PendingAckStore) Collections(java.util.Collections) TransactionInPendingAckStats(org.apache.pulsar.common.policies.data.TransactionInPendingAckStats) NotAllowedException(org.apache.pulsar.broker.service.BrokerServiceException.NotAllowedException) PositionImpl(org.apache.bookkeeper.mledger.impl.PositionImpl) TransactionConflictException(org.apache.pulsar.transaction.common.exception.TransactionConflictException)

Aggregations

TransactionConflictException (org.apache.pulsar.transaction.common.exception.TransactionConflictException)5 PersistentSubscription (org.apache.pulsar.broker.service.persistent.PersistentSubscription)4 TxnID (org.apache.pulsar.client.api.transaction.TxnID)4 ArrayList (java.util.ArrayList)3 List (java.util.List)3 Position (org.apache.bookkeeper.mledger.Position)3 PositionImpl (org.apache.bookkeeper.mledger.impl.PositionImpl)3 MutablePair (org.apache.commons.lang3.tuple.MutablePair)3 Collections (java.util.Collections)2 HashMap (java.util.HashMap)2 Map (java.util.Map)2 BlockingQueue (java.util.concurrent.BlockingQueue)2 CompletableFuture (java.util.concurrent.CompletableFuture)2 ConcurrentSkipListMap (java.util.concurrent.ConcurrentSkipListMap)2 ExecutorService (java.util.concurrent.ExecutorService)2 LinkedBlockingDeque (java.util.concurrent.LinkedBlockingDeque)2 ScheduledExecutorService (java.util.concurrent.ScheduledExecutorService)2 Getter (lombok.Getter)2 Slf4j (lombok.extern.slf4j.Slf4j)2 ManagedLedger (org.apache.bookkeeper.mledger.ManagedLedger)2