public async Task HandleAsync(CreateDeliveryRequestCommand command, CancellationToken cancellationToken) { await requestValidator.ValidateAndThrowAsync(command, cancellationToken : cancellationToken).ConfigureAwait(false); using (var session = documentStore.OpenAsyncSession()) { var deliveryDocument = new Delivery { Id = DocumentIdHelper.GetDocumentId <Delivery>(documentStore, command.TransactionId), Status = DeliveryStatus.Created, Address = new Address { Country = command.Address.Country, City = command.Address.City, House = command.Address.House, State = command.Address.State, Street = command.Address.Street, Zip = command.Address.Zip } }; await session.StoreAsync(deliveryDocument, cancellationToken).ConfigureAwait(false); await session.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } }
protected async Task PerformRollbackStepAsync( string transactionId, Expression <Func <TTransaction, StepDetails> > stepSelector, Func <CancellationToken, Task> rollbackInvoker, CancellationToken cancellationToken) { using (var session = DocumentStore.OpenAsyncSession()) { var transactionDocumentId = DocumentIdHelper.GetDocumentId <TTransaction>(DocumentStore, transactionId); var transactionDocument = await session.LoadAsync <TTransaction>(transactionDocumentId, cancellationToken).ConfigureAwait(false); var stepDetails = stepSelector.Compile()(transactionDocument); var stepStatus = stepDetails.StepStatus; var rollbackAttempts = stepDetails.RollbackAttempts; var transactionStatus = transactionDocument.TransactionStatus; if (stepStatus == StepStatus.RolledBack || stepStatus == StepStatus.NotStarted) { return; } try { await rollbackInvoker(cancellationToken).ConfigureAwait(false); stepStatus = StepStatus.RolledBack; } catch { ++rollbackAttempts; if (rollbackAttempts > MaxRollbackAttemptsPerStep) { stepStatus = StepStatus.RollbackFailed; transactionStatus = TransactionStatus.RollbackFailed; } } var rollbackAttemptsMemberExpression = Expression.MakeMemberAccess(stepSelector, RollbackAttemptsPropertyInfo); var stepStatusMemberExpression = Expression.MakeMemberAccess(stepSelector, StepStatusPropertyInfo); var rollbackAttemptsMemberAccessor = Expression.Lambda <Func <TTransaction, int> >(rollbackAttemptsMemberExpression, stepSelector.Parameters); var stepStatusMemberAccessor = Expression.Lambda <Func <TTransaction, StepStatus> >(stepStatusMemberExpression, stepSelector.Parameters); session.Advanced.Patch(transactionDocument, t => t.TransactionStatus, transactionStatus); session.Advanced.Patch(transactionDocument, rollbackAttemptsMemberAccessor, rollbackAttempts); session.Advanced.Patch(transactionDocument, stepStatusMemberAccessor, stepStatus); if (stepStatus != StepStatus.RolledBack) { session.Advanced.Patch(transactionDocument, t => t.UtcDoNotExecuteBefore, DateTime.UtcNow.AddSeconds(DefaultRetryDelaySeconds)); } await session.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } }
protected async Task <TTransaction> GetTransactionByIdAsync(string transactionId, CancellationToken cancellationToken) { using (var session = DocumentStore.OpenAsyncSession()) { var transactionDocumentId = DocumentIdHelper.GetDocumentId <TTransaction>(DocumentStore, transactionId); var transactionDocument = await session.LoadAsync <TTransaction>(transactionDocumentId, cancellationToken).ConfigureAwait(false); return(transactionDocument); } }
protected async Task CompleteTransactionAsync(string transactionId, CancellationToken cancellationToken) { using (var session = DocumentStore.OpenAsyncSession()) { var transactionDocumentId = DocumentIdHelper.GetDocumentId <TTransaction>(DocumentStore, transactionId); var transactionDocument = await session.LoadAsync <TTransaction>(transactionDocumentId, cancellationToken).ConfigureAwait(false); session.Advanced.Patch(transactionDocument, t => t.TransactionStatus, TransactionStatus.Completed); await session.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } }
public async Task <string> HandleAsync(RegisterOrderCommand command, CancellationToken cancellationToken) { var transactionId = guidProvider.GenerateGuidString(); using (var session = documentStore.OpenAsyncSession()) { var transactionDocumentId = DocumentIdHelper.GetDocumentId <OrderTransaction>(documentStore, transactionId); var transactionDocument = new OrderTransaction { Id = transactionDocumentId, TransactionStatus = TransactionStatus.NotStarted, LoyaltyPointsConsumptionStepDetails = new StepDetails { Attempts = 0, RollbackAttempts = 0, StepStatus = StepStatus.NotStarted }, DeliveryCreationStepDetails = new StepDetails { Attempts = 0, RollbackAttempts = 0, StepStatus = StepStatus.NotStarted }, InventoryReservationStepDetails = new StepDetails { Attempts = 0, RollbackAttempts = 0, StepStatus = StepStatus.NotStarted }, OrderTotalStepDetails = new OrderTotalStepDetails { Attempts = 0, RollbackAttempts = 0, StepStatus = StepStatus.NotStarted, Total = 0 }, OrderDetails = new OrderDetails { UserId = command.UserId, Address = AddressMapper.ToEntity(command.Address), Items = OrderMapper.ToEntities(command.Order) } }; await session.StoreAsync(transactionDocument, cancellationToken).ConfigureAwait(false); await session.SaveChangesAsync().ConfigureAwait(false); } return(transactionId); }
private async Task RollbackAsync(string transactionId, CancellationToken cancellationToken) { await RollbackLoyaltyPointsConsumptionAsync(transactionId, cancellationToken).ConfigureAwait(false); await RollbackInventoryReservationAsync(transactionId, cancellationToken).ConfigureAwait(false); await RollbackDeliveryCreationAsync(transactionId, cancellationToken).ConfigureAwait(false); using (var session = DocumentStore.OpenAsyncSession()) { var transactionDocumentId = DocumentIdHelper.GetDocumentId <OrderTransaction>(DocumentStore, transactionId); var transactionDocument = await session.LoadAsync <OrderTransaction>(transactionDocumentId, cancellationToken).ConfigureAwait(false); if (transactionDocument.TransactionStatus == TransactionStatus.RolledBack) { return; } session.Advanced.Patch(transactionDocument, t => t.TransactionStatus, TransactionStatus.RolledBack); await session.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } }
protected async Task PerformTransactionStepAsync( string transactionId, Expression <Func <TTransaction, StepDetails> > stepSelector, Func <CancellationToken, Task <StepResult> > stepInvoker, CancellationToken cancellationToken) { using (var session = DocumentStore.OpenAsyncSession()) { var transactionDocumentId = DocumentIdHelper.GetDocumentId <TTransaction>(DocumentStore, transactionId); var transactionDocument = await session.LoadAsync <TTransaction>(transactionDocumentId, cancellationToken).ConfigureAwait(false); var stepDetails = stepSelector.Compile()(transactionDocument); if (stepDetails.StepStatus == StepStatus.Completed || stepDetails.StepStatus == StepStatus.PermanentFailure || stepDetails.StepStatus == StepStatus.RollbackFailed || stepDetails.StepStatus == StepStatus.RolledBack) { return; } if (transactionDocument.TransactionStatus != TransactionStatus.NotStarted || transactionDocument.TransactionStatus != TransactionStatus.InProgress) { return; } StepResult stepResult; var stepStatus = StepStatus.InProgress; var attempts = stepDetails.Attempts; try { stepResult = await stepInvoker(cancellationToken).ConfigureAwait(false); } catch { stepResult = StepResult.Retry; } if (stepResult == StepResult.Retry) { ++attempts; stepStatus = StepStatus.TemporalFailure; if (stepDetails.Attempts > MaxAttemptsPerStep) { stepStatus = StepStatus.PermanentFailure; } } else if (stepResult == StepResult.Abort) { stepStatus = StepStatus.PermanentFailure; } else { stepStatus = StepStatus.Completed; } var attemptsMemberExpression = Expression.MakeMemberAccess(stepSelector, RollbackAttemptsPropertyInfo); var stepStatusMemberExpression = Expression.MakeMemberAccess(stepSelector, StepStatusPropertyInfo); var attemptsMemberAccessor = Expression.Lambda <Func <TTransaction, int> >(attemptsMemberExpression, stepSelector.Parameters); var stepStatusMemberAccessor = Expression.Lambda <Func <TTransaction, StepStatus> >(stepStatusMemberExpression, stepSelector.Parameters); session.Advanced.Patch(transactionDocument, attemptsMemberAccessor, attempts); session.Advanced.Patch(transactionDocument, stepStatusMemberAccessor, stepStatus); if (stepStatus == StepStatus.PermanentFailure) { session.Advanced.Patch(transactionDocument, t => t.TransactionStatus, TransactionStatus.PermanentFailure); } if (stepStatus == StepStatus.TemporalFailure) { session.Advanced.Patch(transactionDocument, t => t.UtcDoNotExecuteBefore, DateTime.UtcNow.AddSeconds(DefaultRetryDelaySeconds)); } await session.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } }
private async Task <(int total, StepResult stepResult)> GetAndSaveOrderTotalAsync(string transactionId, CreateOrderCommand command, CancellationToken cancellationToken) { using (var session = DocumentStore.OpenAsyncSession()) { var transactionDocumentId = DocumentIdHelper.GetDocumentId <OrderTransaction>(DocumentStore, transactionId); var transactionDocument = await session.LoadAsync <OrderTransaction>(transactionDocumentId, cancellationToken).ConfigureAwait(false); if (transactionDocument.OrderTotalStepDetails.StepStatus == StepStatus.Completed) { return(transactionDocument.OrderTotalStepDetails.Total, StepResult.Successful); } var totalCost = 0; var attempts = transactionDocument.OrderTotalStepDetails.Attempts; var transactionStatus = transactionDocument.TransactionStatus; var stepStatus = transactionDocument.OrderTotalStepDetails.StepStatus; var stepResult = StepResult.Successful; foreach (var orderItem in command.Order.Items) { try { var product = await catalogApiClient.GetItemAsync(orderItem.ProductId, cancellationToken).ConfigureAwait(false); totalCost += product.Result.PointsCost; } catch (SwaggerException e) when(e.StatusCode == NotFoundStatusCode) { stepResult = StepResult.Abort; break; } catch { stepResult = StepResult.Retry; break; } } if (stepResult == StepResult.Successful) { stepStatus = StepStatus.Completed; } else if (stepResult == StepResult.Retry) { attempts++; stepStatus = StepStatus.TemporalFailure; if (attempts > MaxAttemptsPerStep) { stepStatus = StepStatus.PermanentFailure; stepResult = StepResult.Abort; } } else { stepStatus = StepStatus.PermanentFailure; } if (stepStatus == StepStatus.PermanentFailure) { transactionStatus = TransactionStatus.PermanentFailure; } session.Advanced.Patch(transactionDocument, t => t.TransactionStatus, transactionStatus); session.Advanced.Patch(transactionDocument, t => t.OrderTotalStepDetails.Attempts, attempts); session.Advanced.Patch(transactionDocument, t => t.OrderTotalStepDetails.StepStatus, stepStatus); if (stepStatus == StepStatus.Completed) { session.Advanced.Patch(transactionDocument, t => t.OrderTotalStepDetails.Total, totalCost); } await session.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return(totalCost, stepResult); } }
public static Task <Delivery> LoadDeliveryAsync(this IAsyncDocumentSession session, string id, CancellationToken cancellationToken) { var internalId = DocumentIdHelper.GetDocumentId <Delivery>(session.Advanced.DocumentStore, id); return(session.LoadAsync <Delivery>(internalId, cancellationToken)); }