use of io.pravega.shared.controller.event.CommitEvent in project pravega by pravega.
the class StreamTransactionMetadataTasksTest method idempotentOperationsTests.
@Test(timeout = 10000)
public void idempotentOperationsTests() throws CheckpointStoreException, InterruptedException {
// Create mock writer objects.
EventStreamWriterMock<CommitEvent> commitWriter = new EventStreamWriterMock<>();
EventStreamWriterMock<AbortEvent> abortWriter = new EventStreamWriterMock<>();
EventStreamReader<CommitEvent> commitReader = commitWriter.getReader();
EventStreamReader<AbortEvent> abortReader = abortWriter.getReader();
// Create transaction tasks.
txnTasks = new StreamTransactionMetadataTasks(streamStore, hostStore, segmentHelperMock, executor, "host", connectionFactory, false, "");
txnTasks.initializeStreamWriters("commitStream", commitWriter, "abortStream", abortWriter);
consumer = new ControllerService(streamStore, hostStore, streamMetadataTasks, txnTasks, segmentHelperMock, executor, null);
final ScalingPolicy policy1 = ScalingPolicy.fixed(2);
final StreamConfiguration configuration1 = StreamConfiguration.builder().scope(SCOPE).streamName(STREAM).scalingPolicy(policy1).build();
// Create stream and scope
Assert.assertEquals(Controller.CreateScopeStatus.Status.SUCCESS, consumer.createScope(SCOPE).join().getStatus());
Assert.assertEquals(Controller.CreateStreamStatus.Status.SUCCESS, streamMetadataTasks.createStream(SCOPE, STREAM, configuration1, System.currentTimeMillis()).join());
// Create 2 transactions
final long lease = 5000;
final long scaleGracePeriod = 10000;
VersionedTransactionData txData1 = txnTasks.createTxn(SCOPE, STREAM, lease, scaleGracePeriod, null).join().getKey();
VersionedTransactionData txData2 = txnTasks.createTxn(SCOPE, STREAM, lease, scaleGracePeriod, null).join().getKey();
UUID tx1 = txData1.getId();
UUID tx2 = txData2.getId();
int tx2Version = txData2.getVersion();
// Commit the first one
Assert.assertEquals(TxnStatus.COMMITTING, txnTasks.commitTxn(SCOPE, STREAM, tx1, null).join());
// Ensure that transaction state is COMMITTING.
assertEquals(TxnStatus.COMMITTING, streamStore.transactionStatus(SCOPE, STREAM, tx1, null, executor).join());
// Abort the second one
Assert.assertEquals(TxnStatus.ABORTING, txnTasks.abortTxn(SCOPE, STREAM, tx2, tx2Version, null).join());
// Ensure that transactions state is ABORTING.
assertEquals(TxnStatus.ABORTING, streamStore.transactionStatus(SCOPE, STREAM, tx2, null, executor).join());
// Ensure that commit (resp. abort) transaction tasks are idempotent
// when transaction is in COMMITTING state (resp. ABORTING state).
assertEquals(TxnStatus.COMMITTING, txnTasks.commitTxn(SCOPE, STREAM, tx1, null).join());
assertEquals(TxnStatus.ABORTING, txnTasks.abortTxn(SCOPE, STREAM, tx2, null, null).join());
// Create commit and abort event processors.
ConnectionFactory connectionFactory = Mockito.mock(ConnectionFactory.class);
BlockingQueue<CommitEvent> processedCommitEvents = new LinkedBlockingQueue<>();
BlockingQueue<AbortEvent> processedAbortEvents = new LinkedBlockingQueue<>();
createEventProcessor("commitRG", "commitStream", commitReader, commitWriter, () -> new CommitEventProcessor(streamStore, streamMetadataTasks, hostStore, executor, segmentHelperMock, connectionFactory, processedCommitEvents));
createEventProcessor("abortRG", "abortStream", abortReader, abortWriter, () -> new ConcurrentEventProcessor<>(new AbortRequestHandler(streamStore, streamMetadataTasks, hostStore, executor, segmentHelperMock, connectionFactory, processedAbortEvents), executor));
// Wait until the commit event is processed and ensure that the txn state is COMMITTED.
CommitEvent commitEvent = processedCommitEvents.take();
assertEquals(tx1, commitEvent.getTxid());
assertEquals(TxnStatus.COMMITTED, streamStore.transactionStatus(SCOPE, STREAM, tx1, null, executor).join());
// Wait until the abort event is processed and ensure that the txn state is ABORTED.
AbortEvent abortEvent = processedAbortEvents.take();
assertEquals(tx2, abortEvent.getTxid());
assertEquals(TxnStatus.ABORTED, streamStore.transactionStatus(SCOPE, STREAM, tx2, null, executor).join());
// Ensure that commit (resp. abort) transaction tasks are idempotent
// even after transaction is committed (resp. aborted)
assertEquals(TxnStatus.COMMITTED, txnTasks.commitTxn(SCOPE, STREAM, tx1, null).join());
assertEquals(TxnStatus.ABORTED, txnTasks.abortTxn(SCOPE, STREAM, tx2, null, null).join());
}
use of io.pravega.shared.controller.event.CommitEvent in project pravega by pravega.
the class ControllerEventProcessorTest method testCommitEventProcessorFailedWrite.
@Test(timeout = 10000)
public void testCommitEventProcessorFailedWrite() {
UUID txnId = UUID.randomUUID();
VersionedTransactionData txnData = streamStore.createTransaction(SCOPE, STREAM, txnId, 10000, 10000, 10000, null, executor).join();
CommitEventProcessor commitEventProcessor = spy(new CommitEventProcessor(streamStore, streamMetadataTasks, hostStore, executor, segmentHelperMock, null));
EventProcessor.Writer<CommitEvent> successWriter = event -> CompletableFuture.completedFuture(null);
EventProcessor.Writer<CommitEvent> failedWriter = event -> {
CompletableFuture<Void> future = new CompletableFuture<>();
future.completeExceptionally(new RuntimeException("Error"));
return future;
};
// Simulate a failed write
when(commitEventProcessor.getSelfWriter()).thenReturn(failedWriter).thenReturn(successWriter);
// invoke process with epoch > txnData.
commitEventProcessor.process(new CommitEvent(SCOPE, STREAM, txnData.getEpoch() + 1, txnData.getId()), null);
verify(commitEventProcessor, times(2)).getSelfWriter();
}
use of io.pravega.shared.controller.event.CommitEvent in project pravega by pravega.
the class ControllerEventProcessorTest method testCommitEventProcessor.
@Test(timeout = 10000)
public void testCommitEventProcessor() {
UUID txnId = UUID.randomUUID();
VersionedTransactionData txnData = streamStore.createTransaction(SCOPE, STREAM, txnId, 10000, 10000, 10000, null, executor).join();
Assert.assertNotNull(txnData);
checkTransactionState(SCOPE, STREAM, txnId, TxnStatus.OPEN);
streamStore.sealTransaction(SCOPE, STREAM, txnData.getId(), true, Optional.empty(), null, executor).join();
checkTransactionState(SCOPE, STREAM, txnData.getId(), TxnStatus.COMMITTING);
CommitEventProcessor commitEventProcessor = new CommitEventProcessor(streamStore, streamMetadataTasks, hostStore, executor, segmentHelperMock, null);
commitEventProcessor.process(new CommitEvent(SCOPE, STREAM, txnData.getEpoch(), txnData.getId()), null);
checkTransactionState(SCOPE, STREAM, txnData.getId(), TxnStatus.COMMITTED);
}
use of io.pravega.shared.controller.event.CommitEvent in project pravega by pravega.
the class StreamTransactionMetadataTasks method sealTxnBody.
/**
* Seals a txn and transitions it to COMMITTING (resp. ABORTING) state if commit param is true (resp. false).
*
* Post-condition:
* 1. If seal completes successfully, then
* (a) txn state is COMMITTING/ABORTING,
* (b) CommitEvent/AbortEvent is present in the commit stream/abort stream,
* (c) txn is removed from host-txn index,
* (d) txn is removed from the timeout service.
*
* 2. If process fails after transitioning txn to COMMITTING/ABORTING state, but before responding to client, then
* since txn is present in the host-txn index, some other controller process shall put CommitEvent/AbortEvent to
* commit stream/abort stream.
*
* @param host host id. It is different from hostId iff invoked from TxnSweeper for aborting orphaned txn.
* @param scope scope name.
* @param stream stream name.
* @param commit boolean indicating whether to commit txn.
* @param txnId txn id.
* @param version expected version of txn node in store.
* @param ctx context.
* @return Txn status after sealing it.
*/
CompletableFuture<TxnStatus> sealTxnBody(final String host, final String scope, final String stream, final boolean commit, final UUID txnId, final Integer version, final OperationContext ctx) {
TxnResource resource = new TxnResource(scope, stream, txnId);
Optional<Integer> versionOpt = Optional.ofNullable(version);
// Step 1. Add txn to current host's index, if it is not already present
CompletableFuture<Void> addIndex = host.equals(hostId) && !timeoutService.containsTxn(scope, stream, txnId) ? // then txn would no longer be open.
streamMetadataStore.addTxnToIndex(hostId, resource, Integer.MAX_VALUE) : CompletableFuture.completedFuture(null);
addIndex.whenComplete((v, e) -> {
if (e != null) {
log.debug("Txn={}, already present/newly added to host-txn index of host={}", txnId, hostId);
} else {
log.debug("Txn={}, added txn to host-txn index of host={}", txnId, hostId);
}
});
// Step 2. Seal txn
CompletableFuture<AbstractMap.SimpleEntry<TxnStatus, Integer>> sealFuture = addIndex.thenComposeAsync(x -> streamMetadataStore.sealTransaction(scope, stream, txnId, commit, versionOpt, ctx, executor), executor).whenComplete((v, e) -> {
if (e != null) {
log.debug("Txn={}, failed sealing txn", txnId);
} else {
log.debug("Txn={}, sealed successfully, commit={}", txnId, commit);
}
});
// Step 3. write event to corresponding stream.
return sealFuture.thenComposeAsync(pair -> {
TxnStatus status = pair.getKey();
switch(status) {
case COMMITTING:
return writeCommitEvent(scope, stream, pair.getValue(), txnId, status);
case ABORTING:
return writeAbortEvent(scope, stream, pair.getValue(), txnId, status);
case ABORTED:
case COMMITTED:
return CompletableFuture.completedFuture(status);
case OPEN:
case UNKNOWN:
default:
// exception would be thrown.
return CompletableFuture.completedFuture(status);
}
}, executor).thenComposeAsync(status -> {
// Step 4. Remove txn from timeoutService, and from the index.
timeoutService.removeTxn(scope, stream, txnId);
log.debug("Txn={}, removed from timeout service", txnId);
return streamMetadataStore.removeTxnFromIndex(host, resource, true).whenComplete((v, e) -> {
if (e != null) {
log.debug("Txn={}, failed removing txn from host-txn index of host={}", txnId, hostId);
} else {
log.debug("Txn={}, removed txn from host-txn index of host={}", txnId, hostId);
}
}).thenApply(x -> status);
}, executor);
}
use of io.pravega.shared.controller.event.CommitEvent in project pravega by pravega.
the class StreamTransactionMetadataTasks method writeCommitEvent.
CompletableFuture<TxnStatus> writeCommitEvent(String scope, String stream, int epoch, UUID txnId, TxnStatus status) {
String key = scope + stream;
CommitEvent event = new CommitEvent(scope, stream, epoch, txnId);
return TaskStepsRetryHelper.withRetries(() -> writeEvent(commitEventEventStreamWriter, commitStreamName, key, event, txnId, status), executor);
}
Aggregations