use of io.pravega.controller.store.Version 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 Version version, final String writerId, final long timestamp, final OperationContext ctx) {
Preconditions.checkNotNull(ctx, "Operation context cannot be null");
long requestId = ctx.getRequestId();
TxnResource resource = new TxnResource(scope, stream, txnId);
Optional<Version> 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, version) : CompletableFuture.completedFuture(null);
addIndex.whenComplete((v, e) -> {
if (e != null) {
log.debug(requestId, "Txn={}, already present/newly added to host-txn index of host={}", txnId, hostId);
} else {
log.debug(requestId, "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, writerId, timestamp, ctx, executor), executor).whenComplete((v, e) -> {
if (e != null) {
log.debug(requestId, "Txn={}, failed sealing txn", txnId);
} else {
log.debug(requestId, "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, requestId);
case ABORTING:
return writeAbortEvent(scope, stream, pair.getValue(), txnId, status, requestId);
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(requestId, "Txn={}, removed from timeout service", txnId);
return streamMetadataStore.removeTxnFromIndex(host, resource, true).whenComplete((v, e) -> {
if (e != null) {
log.debug(requestId, "Txn={}, failed removing txn from host-txn index of host={}", txnId, hostId);
} else {
log.debug(requestId, "Txn={}, removed txn from host-txn index of host={}", txnId, hostId);
}
}).thenApply(x -> status);
}, executor);
}
use of io.pravega.controller.store.Version in project pravega by pravega.
the class StreamTransactionMetadataTasks method addTxnToTimeoutService.
private void addTxnToTimeoutService(String scope, String stream, long lease, long maxExecutionPeriod, UUID txnId, CompletableFuture<VersionedTransactionData> txnFuture, long requestId) {
Version version = null;
long executionExpiryTime = System.currentTimeMillis() + maxExecutionPeriod;
if (!txnFuture.isCompletedExceptionally()) {
version = txnFuture.join().getVersion();
executionExpiryTime = txnFuture.join().getMaxExecutionExpiryTime();
}
timeoutService.addTxn(scope, stream, txnId, version, lease, executionExpiryTime);
log.trace(requestId, "Txn={}, added to timeout service on host={}", txnId, hostId);
}
use of io.pravega.controller.store.Version in project pravega by pravega.
the class PersistentStreamBase method createTransaction.
@Override
public CompletableFuture<VersionedTransactionData> createTransaction(final UUID txnId, final long lease, final long maxExecutionTime, OperationContext context) {
Preconditions.checkNotNull(context, "Operation context cannot be null");
final long current = System.currentTimeMillis();
final long leaseTimestamp = current + lease;
final long maxExecTimestamp = current + maxExecutionTime;
// extract epoch from txnid
final int epoch = RecordHelper.getTransactionEpoch(txnId);
ActiveTxnRecord record = ActiveTxnRecord.builder().txnStatus(TxnStatus.OPEN).leaseExpiryTime(leaseTimestamp).txCreationTimestamp(current).maxExecutionExpiryTime(maxExecTimestamp).writerId(Optional.empty()).commitTime(Optional.empty()).commitOrder(Optional.empty()).build();
return verifyNotSealed(context).thenCompose(v -> createNewTransaction(epoch, txnId, record, context).thenApply(version -> new VersionedTransactionData(epoch, txnId, version, TxnStatus.OPEN, current, maxExecTimestamp, "", Long.MIN_VALUE, Long.MIN_VALUE, ImmutableMap.of())));
}
use of io.pravega.controller.store.Version in project pravega by pravega.
the class PersistentStreamBase method sealActiveTxn.
/**
* Seal a transaction in OPEN/COMMITTING_TXN/ABORTING state. This method does CAS on the transaction VersionedMetadata node if
* the transaction is in OPEN state, optionally checking version of transaction VersionedMetadata node, if required.
*
* @param epoch transaction epoch.
* @param txId transaction identifier.
* @param commit boolean indicating whether to commit or abort the transaction.
* @param version optional expected version of transaction node to validate before updating it.
* @param writerId writer Id
* @param timestamp commit timestamp supplied by writer
* @return a pair containing transaction status and its epoch.
*/
private CompletableFuture<SimpleEntry<TxnStatus, Integer>> sealActiveTxn(final int epoch, final UUID txId, final boolean commit, final Optional<Version> version, final String writerId, final long timestamp, OperationContext context) {
return getActiveTx(epoch, txId, context).thenCompose(data -> {
ActiveTxnRecord txnRecord = data.getObject();
Version dataVersion = version.orElseGet(data::getVersion);
TxnStatus status = txnRecord.getTxnStatus();
switch(status) {
case OPEN:
return sealActiveTx(epoch, txId, commit, txnRecord, dataVersion, writerId, timestamp, context).thenApply(y -> new SimpleEntry<>(commit ? TxnStatus.COMMITTING : TxnStatus.ABORTING, epoch));
case COMMITTING:
case COMMITTED:
if (commit) {
return CompletableFuture.completedFuture(new SimpleEntry<>(status, epoch));
} else {
throw StoreException.create(StoreException.Type.ILLEGAL_STATE, "Stream: " + getName() + " Transaction: " + txId.toString() + " State: " + status.name());
}
case ABORTING:
case ABORTED:
if (commit) {
throw StoreException.create(StoreException.Type.ILLEGAL_STATE, "Stream: " + getName() + " Transaction: " + txId.toString() + " State: " + status.name());
} else {
return CompletableFuture.completedFuture(new SimpleEntry<>(status, epoch));
}
default:
throw StoreException.create(StoreException.Type.DATA_NOT_FOUND, "Stream: " + getName() + " Transaction: " + txId.toString());
}
});
}
use of io.pravega.controller.store.Version in project pravega by pravega.
the class PravegaTablesStream method updateWriterMarkRecord.
@Override
CompletableFuture<Void> updateWriterMarkRecord(String writer, long timestamp, ImmutableMap<Long, Long> position, boolean isAlive, Version version, OperationContext context) {
Preconditions.checkNotNull(context, "operation context cannot be null");
WriterMark mark = new WriterMark(timestamp, position, isAlive);
return Futures.toVoid(getWritersTable(context).thenCompose(table -> storeHelper.updateEntry(table, writer, mark, WriterMark::toBytes, version, context.getRequestId())));
}
Aggregations