use of io.pravega.controller.store.stream.records.CommittingTransactionsRecord in project pravega by pravega.
the class ControllerMetadataJsonSerializerTest method testCommittingTransactionsRecord.
@Test
public void testCommittingTransactionsRecord() {
List<UUID> list = Lists.newArrayList(UUID.randomUUID(), UUID.randomUUID());
CommittingTransactionsRecord record0 = new CommittingTransactionsRecord(0, ImmutableList.copyOf(list));
testRecordSerialization(record0, CommittingTransactionsRecord.class);
CommittingTransactionsRecord record = record0.createRollingTxnRecord(10);
testRecordSerialization(record, CommittingTransactionsRecord.class);
}
use of io.pravega.controller.store.stream.records.CommittingTransactionsRecord in project pravega by pravega.
the class CommitRequestHandler method tryCommitTransactions.
/**
* Try creating txn commit list first. if node already exists and doesn't match the processing in the event, throw
* operation not allowed.
* This will result in event being posted back in the stream and retried later. Generally if a transaction commit starts,
* it will come to an end. However, during failover, once we have created the node, we are guaranteed that it will
* be only that transaction that will be getting committed at that time.
* @return CompletableFuture which when completed will contain the epoch on which transactions were committed.
*/
private CompletableFuture<Integer> tryCommitTransactions(final String scope, final String stream, final OperationContext context) {
Timer timer = new Timer();
Map<String, TxnWriterMark> writerMarks = new HashMap<>();
Map<UUID, String> txnIdToWriterId = new HashMap<>();
return streamMetadataStore.getVersionedState(scope, stream, context, executor).thenComposeAsync(state -> {
final AtomicReference<VersionedMetadata<State>> stateRecord = new AtomicReference<>(state);
CompletableFuture<VersionedMetadata<CommittingTransactionsRecord>> commitFuture = streamMetadataStore.startCommitTransactions(scope, stream, MAX_TRANSACTION_COMMIT_BATCH_SIZE, context, executor).thenComposeAsync(txnsTuple -> {
VersionedMetadata<CommittingTransactionsRecord> committingTxnsRecord = txnsTuple.getKey();
if (committingTxnsRecord.getObject().equals(CommittingTransactionsRecord.EMPTY)) {
// completed all the work.
return CompletableFuture.completedFuture(committingTxnsRecord);
} else {
int txnEpoch = committingTxnsRecord.getObject().getEpoch();
List<UUID> txnList = committingTxnsRecord.getObject().getTransactionsToCommit();
log.info(context.getRequestId(), "Committing {} transactions on epoch {} on stream {}/{}", txnList, txnEpoch, scope, stream);
// Once state is set to committing, we are guaranteed that this will be the only
// processing that can happen on the stream and we can proceed with committing
// outstanding transactions collected in the txnList step.
CompletableFuture<Void> future;
// completion of transactions in commit state.
if (state.getObject().equals(State.SEALING)) {
future = CompletableFuture.completedFuture(null);
} else {
// If state is not SEALING, try to set the state to COMMITTING_TXN before proceeding.
// If we are unable to set the state to COMMITTING_TXN, it will get OPERATION_NOT_ALLOWED
// and the processing will be retried later.
future = streamMetadataStore.updateVersionedState(scope, stream, State.COMMITTING_TXN, state, context, executor).thenAccept(stateRecord::set);
}
txnsTuple.getValue().forEach(txn -> {
if (!Strings.isNullOrEmpty(txn.getWriterId())) {
txnIdToWriterId.put(txn.getId(), txn.getWriterId());
if (!writerMarks.containsKey(txn.getWriterId()) || writerMarks.get(txn.getWriterId()).getTimestamp() < txn.getCommitTime()) {
writerMarks.put(txn.getWriterId(), new TxnWriterMark(txn.getCommitTime(), ImmutableMap.of(), txn.getId()));
}
}
});
// TxnCommittingRecord ensures no other rollingTxn can run concurrently
return future.thenCompose(v -> getEpochRecords(scope, stream, txnEpoch, context).thenCompose(records -> {
EpochRecord txnEpochRecord = records.get(0);
EpochRecord activeEpochRecord = records.get(1);
CommitTxnContext commitContext = new CommitTxnContext(scope, stream, context, txnIdToWriterId, writerMarks);
if (activeEpochRecord.getEpoch() == txnEpoch || activeEpochRecord.getReferenceEpoch() == txnEpochRecord.getReferenceEpoch()) {
// we can commit transactions immediately
return commitTransactions(commitContext, committingTxnsRecord, activeEpochRecord.getSegmentIds().stream().collect(Collectors.toList())).thenApply(txnOffsets -> committingTxnsRecord);
} else {
return rollTransactions(commitContext, committingTxnsRecord, txnEpochRecord, activeEpochRecord);
}
}));
}
}, executor);
// reset state to ACTIVE if it was COMMITTING_TXN
return commitFuture.thenCompose(committingTxnsRecord -> streamMetadataStore.completeCommitTransactions(scope, stream, committingTxnsRecord, context, executor, writerMarks).thenCompose(v -> resetStateConditionally(scope, stream, stateRecord.get(), context)).thenRun(() -> TransactionMetrics.getInstance().commitTransaction(scope, stream, timer.getElapsed())).thenApply(v -> committingTxnsRecord.getObject().getEpoch()));
}, executor);
}
use of io.pravega.controller.store.stream.records.CommittingTransactionsRecord in project pravega by pravega.
the class PersistentStreamBase method completeCommittingTransactions.
@Override
public CompletableFuture<Void> completeCommittingTransactions(VersionedMetadata<CommittingTransactionsRecord> record, OperationContext context, Map<String, TxnWriterMark> writerMarks) {
Preconditions.checkNotNull(context, "operation context cannot be null");
// Chain all transaction commit futures one after the other. This will ensure that order of commit
// if honoured and is based on the order in the list.
CompletableFuture<Void> future = generateMarksForTransactions(context, writerMarks);
for (UUID txnId : record.getObject().getTransactionsToCommit()) {
log.debug(context.getRequestId(), "Committing transaction {} on stream {}/{}", txnId, scope, name);
// commit transaction in segment store
future = future.thenCompose(x -> commitTransaction(txnId, context).thenAccept(done -> {
log.debug(context.getRequestId(), "transaction {} on stream {}/{} committed successfully", txnId, scope, name);
}));
}
return future.thenCompose(x -> Futures.toVoid(updateCommittingTxnRecord(new VersionedMetadata<>(CommittingTransactionsRecord.EMPTY, record.getVersion()), context)));
}
use of io.pravega.controller.store.stream.records.CommittingTransactionsRecord in project pravega by pravega.
the class PersistentStreamBase method completeRollingTxn.
@Override
public CompletableFuture<Void> completeRollingTxn(Map<Long, Long> sealedActiveEpochSegments, VersionedMetadata<CommittingTransactionsRecord> versionedMetadata, OperationContext context) {
Preconditions.checkNotNull(context, "Operation context cannot be null");
return getActiveEpoch(true, context).thenCompose(activeEpochRecord -> {
CommittingTransactionsRecord committingTxnRecord = versionedMetadata.getObject();
int activeEpoch = committingTxnRecord.getCurrentEpoch();
if (activeEpochRecord.getEpoch() == activeEpoch) {
return updateSealedSegmentSizes(sealedActiveEpochSegments, context).thenCompose(x -> clearMarkers(sealedActiveEpochSegments.keySet(), context)).thenCompose(x -> updateCurrentEpochRecord(committingTxnRecord.getNewActiveEpoch(), context));
} else {
return CompletableFuture.completedFuture(null);
}
});
}
use of io.pravega.controller.store.stream.records.CommittingTransactionsRecord in project pravega by pravega.
the class PersistentStreamBase method startRollingTxn.
@Override
public CompletableFuture<VersionedMetadata<CommittingTransactionsRecord>> startRollingTxn(int activeEpoch, VersionedMetadata<CommittingTransactionsRecord> existing, OperationContext context) {
Preconditions.checkNotNull(context, "Operation context cannot be null");
CommittingTransactionsRecord record = existing.getObject();
if (record.isRollingTxnRecord()) {
return CompletableFuture.completedFuture(existing);
} else {
CommittingTransactionsRecord update = record.createRollingTxnRecord(activeEpoch);
return updateCommittingTxnRecord(new VersionedMetadata<>(update, existing.getVersion()), context).thenApply(version -> new VersionedMetadata<>(update, version));
}
}
Aggregations