Exemplo n.º 1
0
        public async Task SendQuery <T>(SagaQueryConsumeContext <TSaga, T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next)
            where T : class
        {
            try
            {
                List <TSaga> sagaInstances = await _collection.Find(context.Query.FilterExpression).ToListAsync().ConfigureAwait(false);

                if (!sagaInstances.Any())
                {
                    var missingPipe = new MissingPipe <TSaga, T>(_collection, next, _mongoDbSagaConsumeContextFactory);

                    await policy.Missing(context, missingPipe).ConfigureAwait(false);
                }
                else
                {
                    foreach (var instance in sagaInstances)
                    {
                        await SendToInstance(context, policy, next, instance).ConfigureAwait(false);
                    }
                }
            }
            catch (SagaException sex)
            {
                context.LogFault(sex);

                throw;
            }
            catch (Exception ex)
            {
                context.LogFault(ex);

                throw new SagaException(ex.Message, typeof(TSaga), typeof(T), Guid.Empty, ex);
            }
        }
Exemplo n.º 2
0
        public async Task SendQuery <T>(SagaQueryConsumeContext <TSaga, T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next)
            where T : class
        {
            using (var session = _sessionFactory.OpenSession())
                using (var transaction = session.BeginTransaction())
                {
                    try
                    {
                        IList <TSaga> instances = await session.QueryOver <TSaga>()
                                                  .Where(context.Query.FilterExpression)
                                                  .ListAsync <TSaga>()
                                                  .ConfigureAwait(false);

                        if (instances.Count == 0)
                        {
                            var missingSagaPipe = new MissingPipe <T>(session, next);
                            await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
                        }
                        else
                        {
                            await Task.WhenAll(instances.Select(instance => SendToInstance(context, policy, instance, next, session))).ConfigureAwait(false);
                        }

                        // TODO partial failure should not affect them all

                        if (transaction.IsActive)
                        {
                            await transaction.CommitAsync().ConfigureAwait(false);
                        }
                    }
                    catch (SagaException sex)
                    {
                        context.LogFault(sex);

                        if (transaction.IsActive)
                        {
                            await transaction.RollbackAsync().ConfigureAwait(false);
                        }

                        throw;
                    }
                    catch (Exception ex)
                    {
                        context.LogFault(ex);

                        if (transaction.IsActive)
                        {
                            await transaction.RollbackAsync().ConfigureAwait(false);
                        }

                        throw new SagaException(ex.Message, typeof(TSaga), typeof(T), Guid.Empty, ex);
                    }
                }
        }
Exemplo n.º 3
0
        public async Task SendQuery <T>(SagaQueryConsumeContext <TSaga, T> context, ISagaPolicy <TSaga, T> policy,
                                        IPipe <SagaConsumeContext <TSaga, T> > next)
            where T : class
        {
            using (var session = _store.DirtyTrackedSession())
            {
                try
                {
                    IEnumerable <TSaga> instances = await session.Query <TSaga>()
                                                    .Where(context.Query.FilterExpression)
                                                    .ToListAsync().ConfigureAwait(false);

                    if (!instances.Any())
                    {
                        var missingSagaPipe = new MissingPipe <T>(session, next);
                        await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
                    }
                    else
                    {
                        foreach (var instance in instances)
                        {
                            await SendToInstance(context, policy, instance, next, session).ConfigureAwait(false);
                        }
                    }
                }
                catch (SagaException sex)
                {
                    context.LogFault(sex);

                    throw;
                }
                catch (Exception ex)
                {
                    context.LogFault(ex);

                    throw new SagaException(ex.Message, typeof(TSaga), typeof(T), Guid.Empty, ex);
                }
            }
        }
Exemplo n.º 4
0
        public async Task SendQuery <T>(SagaQueryConsumeContext <TSaga, T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next)
            where T : class
        {
            try
            {
                List <TSaga> sagaInstances = (await _client
                                              .CreateDocumentQuery <TSaga>(UriFactory.CreateDocumentCollectionUri(_databaseName, _collectionName), _feedOptions)
                                              .Where(context.Query.FilterExpression).QueryAsync().ConfigureAwait(false)).ToList();

                if (!sagaInstances.Any())
                {
                    var missingPipe = new MissingPipe <TSaga, T>(_client, _databaseName, _collectionName, next, _documentDbSagaConsumeContextFactory,
                                                                 _jsonSerializerSettings);

                    await policy.Missing(context, missingPipe).ConfigureAwait(false);
                }
                else
                {
                    foreach (var instance in sagaInstances)
                    {
                        await SendToInstance(context, policy, next, instance).ConfigureAwait(false);
                    }
                }
            }
            catch (SagaException sex)
            {
                context.LogFault(sex);

                throw;
            }
            catch (Exception ex)
            {
                context.LogFault(ex);

                throw new SagaException(ex.Message, typeof(TSaga), typeof(T), Guid.Empty, ex);
            }
        }
        async Task SendQueryLogic <T>(IList <Guid> nonTrackedInstances, IDbContextTransaction transaction, DbContext dbContext,
                                      SagaQueryConsumeContext <TSaga, T> context, ISagaPolicy <TSaga, T> policy,
                                      IPipe <SagaConsumeContext <TSaga, T> > next)
            where T : class
        {
            try
            {
                // Simple path for Optimistic Concurrency
                if (_rawSqlLockStatements == null)
                {
                    var instances = await dbContext.Set <TSaga>()
                                    .Where(context.Query.FilterExpression)
                                    .ToListAsync(context.CancellationToken)
                                    .ConfigureAwait(false);

                    if (!instances.Any())
                    {
                        var missingSagaPipe = new MissingPipe <T>(dbContext, next);
                        await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
                    }
                    else
                    {
                        await Task.WhenAll(instances.Select(instance => SendToInstance(context, dbContext, policy, instance, next))).ConfigureAwait(false);
                    }
                }
                // Pessimistic Concurrency
                else
                {
                    // Hack for locking row for the duration of the transaction.
                    // We only lock one at a time, since we don't want an accidental range lock.
                    var rowLockQuery = _rawSqlLockStatements.GetRowLockStatement <TSaga>(dbContext);

                    var missingCorrelationIds = new List <Guid>();
                    if (nonTrackedInstances?.Any() == true)
                    {
                        var foundInstances = new List <Task>();

                        foreach (var nonTrackedInstance in nonTrackedInstances)
                        {
                            // Query with a row Lock instead using FromSql. Still a single trip to the DB (unlike EF6, which has to make one dummy call to row lock)
                            var instance = await dbContext.Set <TSaga>()
                                           .FromSqlRaw(rowLockQuery, new object[] { nonTrackedInstance })
                                           .SingleOrDefaultAsync(context.CancellationToken)
                                           .ConfigureAwait(false);

                            if (instance != null)
                            {
                                foundInstances.Add(SendToInstance(context, dbContext, policy, instance, next));
                            }
                            else
                            {
                                missingCorrelationIds.Add(nonTrackedInstance);
                            }
                        }

                        if (foundInstances.Any())
                        {
                            await Task.WhenAll(foundInstances).ConfigureAwait(false);
                        }
                    }

                    // If no sagas are found or all are missing
                    if (nonTrackedInstances.Count == missingCorrelationIds.Count)
                    {
                        var missingSagaPipe = new MissingPipe <T>(dbContext, next);

                        await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
                    }
                }

                await dbContext.SaveChangesAsync(context.CancellationToken).ConfigureAwait(false);

                transaction.Commit();
            }
            catch (DbUpdateConcurrencyException)
            {
                try
                {
                    transaction.Rollback();
                }
                catch (Exception innerException)
                {
                    LogContext.Warning?.Log(innerException, "Transaction rollback failed");
                }

                throw;
            }
            catch (DbUpdateException ex)
            {
                if (IsDeadlockException(ex))
                {
                    // deadlock, no need to rollback
                }
                else
                {
                    try
                    {
                        transaction.Rollback();
                    }
                    catch (Exception innerException)
                    {
                        LogContext.Warning?.Log(innerException, "Transaction rollback failed");
                    }
                }

                throw;
            }
            catch (SagaException sex)
            {
                context.LogFault(sex);

                try
                {
                    transaction.Rollback();
                }
                catch (Exception innerException)
                {
                    LogContext.Warning?.Log(innerException, "Transaction rollback failed");
                }

                throw;
            }
            catch (Exception ex)
            {
                context.LogFault(ex);

                try
                {
                    transaction.Rollback();
                }
                catch (Exception innerException)
                {
                    LogContext.Warning?.Log(innerException, "Transaction rollback failed");
                }

                throw new SagaException(ex.Message, typeof(TSaga), typeof(T), Guid.Empty, ex);
            }
        }
        public async Task SendQuery <T>(SagaQueryConsumeContext <TSaga, T> context, ISagaPolicy <TSaga, T> policy,
                                        IPipe <SagaConsumeContext <TSaga, T> > next)
            where T : class
        {
            var dbContext = _sagaDbContextFactory.CreateScoped(context);

            try
            {
                List <Guid> nonTrackedInstances = null;

                // Only perform this additional DB Call for pessimistic concurrency
                if (_rawSqlLockStatements != null)
                {
                    // We just get the correlation ids related to our Filter.
                    // We do this outside of the transaction to make sure we don't create a range lock.
                    nonTrackedInstances = await dbContext.Set <TSaga>()
                                          .AsNoTracking()
                                          .Where(context.Query.FilterExpression)
                                          .Select(x => x.CorrelationId)
                                          .ToListAsync(context.CancellationToken)
                                          .ConfigureAwait(false);
                }

                using (var transaction = dbContext.Database.BeginTransaction(_isolationLevel))
                {
                    try
                    {
                        // Simple path for Optimistic Concurrency
                        if (_rawSqlLockStatements == null)
                        {
                            var instances = await QuerySagas(dbContext)
                                            .Where(context.Query.FilterExpression)
                                            .ToListAsync(context.CancellationToken)
                                            .ConfigureAwait(false);

                            if (!instances.Any())
                            {
                                var missingSagaPipe = new MissingPipe <T>(dbContext, next);
                                await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
                            }
                            else
                            {
                                await Task.WhenAll(instances.Select(instance => SendToInstance(context, dbContext, policy, instance, next)))
                                .ConfigureAwait(false);
                            }
                        }
                        // Pessimistic Concurrency
                        else
                        {
                            var rowLockQuery = _rawSqlLockStatements.GetRowLockStatement <TSaga>(dbContext);

                            var missingCorrelationIds = new List <Guid>();

                            if (nonTrackedInstances?.Any() == true)
                            {
                                var foundInstances = new List <Task>();

                                foreach (var nonTrackedInstance in nonTrackedInstances)
                                {
                                    // Hack for locking row for the duration of the transaction.
                                    // We only lock one at a time, since we don't want an accidental range lock.
                                    await dbContext.Database.ExecuteSqlCommandAsync(rowLockQuery, context.CancellationToken, nonTrackedInstance)
                                    .ConfigureAwait(false);

                                    var instance = await QuerySagas(dbContext)
                                                   .SingleOrDefaultAsync(x => x.CorrelationId == nonTrackedInstance, context.CancellationToken)
                                                   .ConfigureAwait(false);

                                    if (instance != null)
                                    {
                                        foundInstances.Add(SendToInstance(context, dbContext, policy, instance, next));
                                    }
                                    else
                                    {
                                        missingCorrelationIds.Add(nonTrackedInstance);
                                    }
                                }

                                if (foundInstances.Any())
                                {
                                    await Task.WhenAll(foundInstances).ConfigureAwait(false);
                                }
                            }

                            // If no sagas are found or all are missing
                            if (nonTrackedInstances.Count == missingCorrelationIds.Count)
                            {
                                var missingSagaPipe = new MissingPipe <T>(dbContext, next);

                                await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
                            }
                        }

                        await dbContext.SaveChangesAsync(context.CancellationToken).ConfigureAwait(false);

                        transaction.Commit();
                    }
                    catch (DbUpdateConcurrencyException)
                    {
                        try
                        {
                            transaction.Rollback();
                        }
                        catch (Exception innerException)
                        {
                            LogContext.Warning?.Log(innerException, "Transaction rollback failed");
                        }

                        throw;
                    }
                    catch (DbUpdateException ex)
                    {
                        if (IsDeadlockException(ex))
                        {
                            // deadlock, no need to rollback
                        }
                        else
                        {
                            try
                            {
                                transaction.Rollback();
                            }
                            catch (Exception innerException)
                            {
                                LogContext.Warning?.Log(innerException, "Transaction rollback failed");
                            }
                        }

                        throw;
                    }
                    catch (SagaException sex)
                    {
                        context.LogFault(sex);

                        try
                        {
                            transaction.Rollback();
                        }
                        catch (Exception innerException)
                        {
                            LogContext.Warning?.Log(innerException, "Transaction rollback failed");
                        }

                        throw;
                    }
                    catch (Exception ex)
                    {
                        try
                        {
                            transaction.Rollback();
                        }
                        catch (Exception innerException)
                        {
                            LogContext.Warning?.Log(innerException, "Transaction rollback failed");
                        }

                        context.LogFault(ex);

                        throw new SagaException(ex.Message, typeof(TSaga), typeof(T), Guid.Empty, ex);
                    }
                }
            }
            finally
            {
                _sagaDbContextFactory.Release(dbContext);
            }
        }
Exemplo n.º 7
0
        public async Task SendQuery <T>(SagaQueryConsumeContext <TSaga, T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next)
            where T : class
        {
            var tableName = GetTableName();

            var(whereStatement, parameters) = WhereStatementHelper.GetWhereStatementAndParametersFromExpression(context.Query.FilterExpression);

            List <Guid> correlationIds = null;

            using (var connection = new SqlConnection(_connectionString))
            {
                correlationIds =
                    (await connection.QueryAsync <Guid>(
                         $"SELECT CorrelationId FROM {tableName} WITH (NOLOCK) {whereStatement}",
                         parameters).ConfigureAwait(false)).ToList();
            }

            using (var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                using (var connection = new SqlConnection(_connectionString))
                {
                    try
                    {
                        var missingCorrelationIds = new List <Guid>();

                        foreach (var correlationId in correlationIds)
                        {
                            var instance =
                                await connection.QuerySingleOrDefaultAsync <TSaga>(
                                    $"SELECT * FROM {tableName} WITH (UPDLOCK, ROWLOCK) where CorrelationId = @correlationId",
                                    new { correlationId }).ConfigureAwait(false);

                            if (instance != null)
                            {
                                await SendToInstance(context, connection, policy, instance, tableName, next).ConfigureAwait(false);
                            }
                            else
                            {
                                missingCorrelationIds.Add(correlationId);
                            }
                        }

                        // If no sagas are found or all are missing
                        if (correlationIds.Count == missingCorrelationIds.Count)
                        {
                            var missingSagaPipe = new MissingPipe <T>(connection, tableName, next, InsertSagaInstance);

                            await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
                        }

                        transaction.Complete();
                    }
                    catch (SagaException sex)
                    {
                        context.LogFault(sex);
                        throw;
                    }
                    catch (Exception ex)
                    {
                        context.LogFault(ex);

                        throw new SagaException(ex.Message, typeof(TSaga), typeof(T), Guid.Empty, ex);
                    }
                }
        }