/// <summary> /// Try to abort the transaction. /// </summary> /// /// <param name="transaction"> /// The transaction to abort. /// </param> /// <param name="cancellationToken"> /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// </param> /// /// <returns>Whether the abort call has succeeded.</returns> private async Task <bool> TryAbort(AsyncTransaction transaction, CancellationToken cancellationToken) { try { if (transaction != null) { await transaction.Abort(); } else { await this.session.AbortTransactionAsync(cancellationToken); } } catch (Exception e) { this.logger.LogWarning("This session is invalid on ABORT: {}", e); return(false); } return(true); }
/// <summary> /// Initializes a new instance of the <see cref="AsyncTransactionExecutor"/> class. /// </summary> /// /// <param name="transaction"> /// The <see cref="AsyncTransaction"/> object the <see cref="AsyncTransactionExecutor"/> wraps. /// </param> /// <param name="serializer">The serializer to serialize and deserialize Ion data.</param> internal AsyncTransactionExecutor(AsyncTransaction transaction, ISerializer serializer = null) { this.transaction = transaction; this.serializer = serializer; }
/// <summary> /// Initializes a new instance of the <see cref="AsyncTransactionExecutor"/> class. /// </summary> /// /// <param name="transaction"> /// The <see cref="AsyncTransaction"/> object the <see cref="AsyncTransactionExecutor"/> wraps. /// </param> internal AsyncTransactionExecutor(AsyncTransaction transaction) { this.transaction = transaction; }
/// <summary> /// Execute the Executor lambda asynchronously against QLDB and retrieve the result within a transaction. /// </summary> /// /// <param name="func">The Executor lambda representing the block of code to be executed within the transaction. /// This cannot have any side effects as it may be invoked multiple times, and the result cannot be trusted /// until the transaction is committed.</param> /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive /// notice of cancellation.</param> /// <typeparam name="T">The return type.</typeparam> /// /// <returns>The return value of executing the executor. Note that if you directly return a /// <see cref="IAsyncResult"/>, this will be automatically buffered in memory before the implicit commit to /// allow reading, as the commit will close any open results. Any other <see cref="IAsyncResult"/> instances /// created within the executor block will be invalidated, including if the return value is an object which /// nests said <see cref="IAsyncResult"/> instances within it. /// </returns> /// /// <exception cref="TransactionAbortedException"> /// Thrown if the Executor lambda calls <see cref="AsyncTransactionExecutor.Abort"/>. /// </exception> /// <exception cref="AmazonServiceException"> /// Thrown when there is an error executing against QLDB. /// </exception> internal async Task <T> Execute <T>( Func <AsyncTransactionExecutor, Task <T> > func, CancellationToken cancellationToken) { ValidationUtils.AssertNotNull(func, "func"); AsyncTransaction transaction = null; string transactionId = QldbTransactionException.DefaultTransactionId; try { transaction = await this.StartTransaction(cancellationToken); transactionId = transaction.Id; T returnedValue = await func(new AsyncTransactionExecutor(transaction)); if (returnedValue is IAsyncResult result) { returnedValue = (T)(object)(await AsyncBufferedResult.BufferResultAsync(result)); } await transaction.Commit(); return(returnedValue); } catch (TransactionAbortedException) { throw; } catch (InvalidSessionException ise) { if (IsTransactionExpiredException(ise)) { throw new QldbTransactionException( transactionId, await this.TryAbort(transaction, cancellationToken), ise); } else { throw new RetriableException(transactionId, false, ise); } } catch (OccConflictException occ) { throw new RetriableException(transactionId, true, occ); } catch (AmazonServiceException ase) { if (ase.StatusCode == HttpStatusCode.InternalServerError || ase.StatusCode == HttpStatusCode.ServiceUnavailable) { throw new RetriableException( transactionId, await this.TryAbort(transaction, cancellationToken), ase); } throw new QldbTransactionException( transactionId, await this.TryAbort(transaction, cancellationToken), ase); } catch (Exception e) { throw new QldbTransactionException( transactionId, await this.TryAbort(transaction, cancellationToken), e); } finally { transaction?.Dispose(); } }