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);
}
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));
}
}
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));
}
}
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;
});
}
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;
});
}
Aggregations