Esempio 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);
            }
        }
        public async Task Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) where T : class
        {
            if (!context.CorrelationId.HasValue)
            {
                throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T));
            }

            TSaga instance;

            if (policy.PreInsertInstance(context, out instance))
            {
                await PreInsertSagaInstance(context, instance).ConfigureAwait(false);
            }

            if (instance == null)
            {
                instance =
                    await _collection.Find(x => x.CorrelationId == context.CorrelationId).SingleOrDefaultAsync(context.CancellationToken).ConfigureAwait(false);
            }

            if (instance == null)
            {
                var missingSagaPipe = new MissingPipe <TSaga, T>(_collection, next, _mongoDbSagaConsumeContextFactory);

                await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
            }
            else
            {
                await SendToInstance(context, policy, next, instance).ConfigureAwait(false);
            }
        }
Esempio 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 = _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)
                    {
                        if (_log.IsErrorEnabled)
                        {
                            _log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", sex);
                        }

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

                        throw;
                    }
                    catch (Exception ex)
                    {
                        if (_log.IsErrorEnabled)
                        {
                            _log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", ex);
                        }

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

                        throw new SagaException(ex.Message, typeof(TSaga), typeof(T), Guid.Empty, ex);
                    }
                }
        }
Esempio n. 4
0
        public async Task Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) where T : class
        {
            if (!context.CorrelationId.HasValue)
            {
                throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T));
            }

            var sagaId = context.CorrelationId.Value;

            if (policy.PreInsertInstance(context, out var instance))
            {
                await PreInsertSagaInstance <T>(instance, context.MessageId).ConfigureAwait(false);
            }

            if (instance == null)
            {
                instance = await GetSaga(sagaId);
            }

            if (instance == null)
            {
                var missingSagaPipe = new MissingPipe <T>(_connection, _options, next);
                await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
            }
            else
            {
                await SendToInstance(context, policy, next, instance).ConfigureAwait(false);
            }
        }
Esempio n. 5
0
        async Task ISagaRepository <TSaga> .Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy,
                                                     IPipe <SagaConsumeContext <TSaga, T> > next)
        {
            if (!context.CorrelationId.HasValue)
            {
                throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T));
            }

            Guid sagaId = context.CorrelationId.Value;

            using (DbContext dbContext = _sagaDbContextFactory())
                using (var transaction = dbContext.Database.BeginTransaction(_isolationLevel))
                {
                    bool inserted = false;

                    TSaga instance;
                    if (policy.PreInsertInstance(context, out instance))
                    {
                        inserted = await PreInsertSagaInstance <T>(dbContext, instance, context.CancellationToken).ConfigureAwait(false);
                    }

                    try
                    {
                        if (instance == null)
                        {
                            instance = dbContext.Set <TSaga>().SingleOrDefault(x => x.CorrelationId == sagaId);
                        }
                        if (instance == null)
                        {
                            var missingSagaPipe = new MissingPipe <T>(dbContext, next);

                            await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
                        }
                        else
                        {
                            if (_log.IsDebugEnabled)
                            {
                                _log.DebugFormat("SAGA:{0}:{1} Used {2}", TypeMetadataCache <TSaga> .ShortName, instance.CorrelationId,
                                                 TypeMetadataCache <T> .ShortName);
                            }

                            var sagaConsumeContext = new EntityFrameworkSagaConsumeContext <TSaga, T>(dbContext, context, instance);

                            await policy.Existing(sagaConsumeContext, next).ConfigureAwait(false);

//                        if (inserted && !sagaConsumeContext.IsCompleted)
//                            dbContext.Set<TSaga>().Update(instance);
                        }

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

                        transaction.Commit();
                    }
                    catch (Exception)
                    {
                        transaction.Rollback();
                        throw;
                    }
                }
        }
Esempio n. 6
0
        async Task ISagaRepository <TSaga> .Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy,
                                                     IPipe <SagaConsumeContext <TSaga, T> > next)
        {
            if (!context.CorrelationId.HasValue)
            {
                throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T));
            }

            var sagaId = context.CorrelationId.Value;

            using (var session = _store.DirtyTrackedSession())
            {
                TSaga instance;
                if (policy.PreInsertInstance(context, out instance))
                {
                    PreInsertSagaInstance <T>(session, instance);
                }

                if (instance == null)
                {
                    instance = session.Load <TSaga>(sagaId);
                }

                if (instance == null)
                {
                    var missingSagaPipe = new MissingPipe <T>(session, next);
                    await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
                }
                else
                {
                    await SendToInstance(context, policy, instance, next, session).ConfigureAwait(false);
                }
            }
        }
Esempio n. 7
0
        public void GivenAMissingPipe()
        {
            _probeContext = new Mock <ProbeContext>();

            _nextPipe = new Mock <IPipe <SagaConsumeContext <SimpleSaga, InitiateSimpleSaga> > >();

            _pipe = new MissingPipe <SimpleSaga, InitiateSimpleSaga>(Mock.Of <IMongoCollection <SimpleSaga> >(), _nextPipe.Object, Mock.Of <IMongoDbSagaConsumeContextFactory>());
        }
Esempio n. 8
0
        public void GivenAMissingPipe()
        {
            _probeContext = new Mock <ProbeContext>();

            _nextPipe = new Mock <IPipe <SagaConsumeContext <SimpleSaga, InitiateSimpleSaga> > >();

            _pipe = new MissingPipe <SimpleSaga, InitiateSimpleSaga>(Mock.Of <IDocumentClient>(), SagaRepository.DatabaseName, SagaRepository.CollectionName, _nextPipe.Object,
                                                                     Mock.Of <IDocumentDbSagaConsumeContextFactory>(), new RequestOptions());
        }
        public void GivenAMissingPipe()
        {
            _probeContext = new Mock<ProbeContext>();

            _nextPipe = new Mock<IPipe<SagaConsumeContext<SimpleSaga, InitiateSimpleSaga>>>();

            _pipe = new MissingPipe<SimpleSaga, InitiateSimpleSaga>(Mock.Of<IMongoCollection<SimpleSaga>>(), _nextPipe.Object,
                Mock.Of<IMongoDbSagaConsumeContextFactory>());
        }
Esempio n. 10
0
        async Task ISagaRepository <TSaga> .Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next)
        {
            if (!context.CorrelationId.HasValue)
            {
                throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T));
            }

            var sagaId = context.CorrelationId.Value;

            var needToLeaveSagas = true;
            await _sagas.MarkInUse(context.CancellationToken).ConfigureAwait(false);

            try
            {
                SagaInstance <TSaga> saga = _sagas[sagaId];
                if (saga == null)
                {
                    var missingSagaPipe = new MissingPipe <T>(this, next, true);

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

                    _sagas.Release();
                    needToLeaveSagas = false;
                }
                else
                {
                    await saga.MarkInUse(context.CancellationToken).ConfigureAwait(false);

                    try
                    {
                        _sagas.Release();
                        needToLeaveSagas = false;

                        if (_log.IsDebugEnabled)
                        {
                            _log.DebugFormat("SAGA:{0}:{1} Used {2}", TypeMetadataCache <TSaga> .ShortName, sagaId, TypeMetadataCache <T> .ShortName);
                        }

                        SagaConsumeContext <TSaga, T> sagaConsumeContext = new InMemorySagaConsumeContext <TSaga, T>(context, saga.Instance,
                                                                                                                     () => Remove(saga, context.CancellationToken));

                        await policy.Existing(sagaConsumeContext, next).ConfigureAwait(false);
                    }
                    finally
                    {
                        saga.Release();
                    }
                }
            }
            finally
            {
                if (needToLeaveSagas)
                {
                    _sagas.Release();
                }
            }
        }
Esempio n. 11
0
        async Task ISagaRepository <TSaga> .Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next)
        {
            if (!context.CorrelationId.HasValue)
            {
                throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T));
            }

            var correlationId = context.CorrelationId.Value;

            if (policy.PreInsertInstance(context, out TSaga preInsertInstance))
            {
                using (var connection = new SqlConnection(_connectionString))
                {
                    await PreInsertSagaInstance <T>(connection, context, preInsertInstance).ConfigureAwait(false);
                }
            }

            using (var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                using (var connection = new SqlConnection(_connectionString))
                {
                    try
                    {
                        var tableName = GetTableName();

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

                        if (instance == null)
                        {
                            var missingSagaPipe = new MissingPipe <T>(connection, tableName, next, InsertSagaInstance);

                            await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
                        }
                        else
                        {
                            await SendToInstance(context, connection, policy, instance, tableName, next).ConfigureAwait(false);
                        }

                        transaction.Complete();
                    }
                    catch (SagaException sex)
                    {
                        LogContext.Error?.Log(sex, "SAGA:{SagaType} Exception {MessageType}", TypeMetadataCache <TSaga> .ShortName, TypeMetadataCache <T> .ShortName);
                        throw;
                    }
                    catch (Exception ex)
                    {
                        LogContext.Error?.Log(ex, "SAGA:{SagaType} Exception {MessageType}", TypeMetadataCache <TSaga> .ShortName, TypeMetadataCache <T> .ShortName);

                        throw new SagaException(ex.Message, typeof(TSaga), typeof(T), correlationId, ex);
                    }
                }
        }
        Task ISagaRepository <TSaga> .SendQuery <T>(SagaQueryConsumeContext <TSaga, T> context, ISagaPolicy <TSaga, T> policy,
                                                    IPipe <SagaConsumeContext <TSaga, T> > next)
        {
            SagaInstance <TSaga>[] existingSagas = _sagas.Where(context.Query).ToArray();
            if (existingSagas.Length == 0)
            {
                var missingSagaPipe = new MissingPipe <T>(this, next);
                return(policy.Missing(context, missingSagaPipe));
            }

            return(Task.WhenAll(existingSagas.Select(instance => SendToInstance(context, policy, instance, next))));
        }
        async Task ISagaRepository <TSaga> .Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy,
                                                     IPipe <SagaConsumeContext <TSaga, T> > next)
        {
            if (!context.CorrelationId.HasValue)
            {
                throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T));
            }

            var sagaId = context.CorrelationId.Value;
            var db     = _redisDbFactory();

            ITypedDatabase <TSaga> sagas = db.As <TSaga>();

            IAsyncDisposable pessimisticLock = null;

            if (!_optimistic)
            {
                pessimisticLock = await db.AcquireLockAsync(sagaId, _lockTimeout, _lockRetryTimeout).ConfigureAwait(false);
            }

            try
            {
                if (policy.PreInsertInstance(context, out var instance))
                {
                    await PreInsertSagaInstance <T>(sagas, instance).ConfigureAwait(false);
                }

                if (instance == null)
                {
                    instance = await sagas.Get(sagaId).ConfigureAwait(false);
                }

                if (instance == null)
                {
                    var missingSagaPipe = new MissingPipe <T>(sagas, next);

                    await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
                }
                else
                {
                    await SendToInstance(sagas, context, policy, next, instance).ConfigureAwait(false);
                }
            }
            finally
            {
                if (!_optimistic)
                {
                    await pessimisticLock.DisposeAsync().ConfigureAwait(false);
                }
            }
        }
        public void GivenAMissingPipe_WhenSendingAndProxyCompleted()
        {
            _nextPipe = new Mock <IPipe <SagaConsumeContext <SimpleSaga, InitiateSimpleSaga> > >();
            _proxy    = new Mock <SagaConsumeContext <SimpleSaga, InitiateSimpleSaga> >();
            _proxy.SetupGet(m => m.IsCompleted).Returns(true);
            _consumeContextFactory = new Mock <IDocumentDbSagaConsumeContextFactory>();
            _mockDocumentClient    = new Mock <IDocumentClient>();
            _context = new Mock <SagaConsumeContext <SimpleSaga, InitiateSimpleSaga> >();
            _consumeContextFactory.Setup(m => m.Create(_mockDocumentClient.Object, "", "", _context.Object, It.IsAny <SimpleSaga>(), false, null)).Returns(_proxy.Object);

            _pipe = new MissingPipe <SimpleSaga, InitiateSimpleSaga>(_mockDocumentClient.Object, "", "", _nextPipe.Object, _consumeContextFactory.Object, null);

            TaskUtil.Await(() => _pipe.Send(_context.Object));
        }
        public void GivenAMissingPipe_WhenSendingAndProxyCompleted()
        {
            _collection = new Mock <IMongoCollection <SimpleSaga> >();
            _nextPipe   = new Mock <IPipe <SagaConsumeContext <SimpleSaga, InitiateSimpleSaga> > >();
            _proxy      = new Mock <SagaConsumeContext <SimpleSaga, InitiateSimpleSaga> >();
            _proxy.SetupGet(m => m.IsCompleted).Returns(true);
            _consumeContextFactory = new Mock <IMongoDbSagaConsumeContextFactory>();
            _context = new Mock <SagaConsumeContext <SimpleSaga, InitiateSimpleSaga> >();
            _consumeContextFactory.Setup(m => m.Create(_collection.Object, _context.Object, _context.Object.Saga, false)).Returns(_proxy.Object);

            _pipe = new MissingPipe <SimpleSaga, InitiateSimpleSaga>(_collection.Object, _nextPipe.Object, _consumeContextFactory.Object);

            TaskUtil.Await(() => _pipe.Send(_context.Object));
        }
Esempio n. 16
0
        public async Task SendQuery <T>(SagaQueryConsumeContext <TSaga, T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) where T : class
        {
            using (var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                using (var connection = new SqlConnection(_connectionString))
                {
                    try
                    {
                        var tableName = GetTableName <T>();
                        var(whereStatement, parameters) = WhereStatementHelper.GetWhereStatementAndParametersFromExpression(context.Query.FilterExpression);

                        List <TSaga> instances =
                            (await connection.QueryAsync <TSaga>($"SELECT * FROM {tableName} WITH (UPDLOCK, ROWLOCK) {whereStatement}",
                                                                 parameters).ConfigureAwait(false)).ToList();

                        if (!instances.Any())
                        {
                            var missingSagaPipe = new MissingPipe <T>(connection, tableName, next, InsertSagaInstance);

                            await policy.Missing(context, missingSagaPipe);
                        }
                        else
                        {
                            foreach (var instance in instances)
                            {
                                await SendToInstance(context, connection, policy, instance, tableName, next).ConfigureAwait(false);
                            }
                        }

                        transaction.Complete();
                    }
                    catch (SagaException sex)
                    {
                        if (Log.IsErrorEnabled)
                        {
                            Log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", sex);
                        }

                        throw;
                    }
                    catch (Exception ex)
                    {
                        if (Log.IsErrorEnabled)
                        {
                            Log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", ex);
                        }

                        throw new SagaException(ex.Message, typeof(TSaga), typeof(T), Guid.Empty, ex);
                    }
                }
        }
        public void GivenAMissingPipe_WhenSendingAndProxyCompleted()
        {
            _collection = new Mock<IMongoCollection<SimpleSaga>>();
            _nextPipe = new Mock<IPipe<SagaConsumeContext<SimpleSaga, InitiateSimpleSaga>>>();
            _proxy = new Mock<SagaConsumeContext<SimpleSaga, InitiateSimpleSaga>>();
            _proxy.SetupGet(m => m.IsCompleted).Returns(true);
            _consumeContextFactory = new Mock<IMongoDbSagaConsumeContextFactory>();
            _context = new Mock<SagaConsumeContext<SimpleSaga, InitiateSimpleSaga>>();
            _consumeContextFactory.Setup(m => m.Create(_collection.Object, _context.Object, _context.Object.Saga, false)).Returns(_proxy.Object);

            _pipe = new MissingPipe<SimpleSaga, InitiateSimpleSaga>(_collection.Object, _nextPipe.Object, _consumeContextFactory.Object);

            TaskUtil.Await(() => _pipe.Send(_context.Object));
        }
        async Task ISagaRepository <TSaga> .Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next)
        {
            if (!context.TryGetPayload(out MessageSessionContext sessionContext))
            {
                throw new SagaException($"The session-based saga repository requires an active message session: {TypeMetadataCache<TSaga>.ShortName}",
                                        typeof(TSaga), typeof(T));
            }

            if (Guid.TryParse(sessionContext.SessionId, out var sessionId))
            {
                context = new CorrelationIdConsumeContextProxy <T>(context, sessionId);
            }

            StartedActivity?activity = LogContext.IfEnabled(OperationName.Saga.Send)?.StartSagaActivity <TSaga, T>(context);

            try
            {
                var saga = await ReadSagaState(sessionContext).ConfigureAwait(false);

                if (saga == null)
                {
                    var missingSagaPipe = new MissingPipe <T>(next, WriteSagaState);

                    await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
                }
                else
                {
                    SagaConsumeContext <TSaga, T> sagaConsumeContext = new MessageSessionSagaConsumeContext <TSaga, T>(context, sessionContext, saga);

                    LogContext.Debug?.Log("SAGA:{SagaType}:{CorrelationId} Used {MessageType}", TypeMetadataCache <TSaga> .ShortName,
                                          context.CorrelationId, TypeMetadataCache <T> .ShortName);

                    await policy.Existing(sagaConsumeContext, next).ConfigureAwait(false);

                    if (!sagaConsumeContext.IsCompleted)
                    {
                        await WriteSagaState(sessionContext, saga).ConfigureAwait(false);

                        LogContext.Debug?.Log("SAGA:{SagaType}:{CorrelationId} Updated {MessageType}", TypeMetadataCache <TSaga> .ShortName,
                                              context.CorrelationId, TypeMetadataCache <T> .ShortName);
                    }
                }
            }
            finally
            {
                activity?.Stop();
            }
        }
Esempio n. 19
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 = OpenSession())
            {
                try
                {
                    var guids = (await Find(context.Query).ConfigureAwait(false)).ToArray();

                    if (!guids.Any())
                    {
                        var missingSagaPipe = new MissingPipe <T>(session, next);
                        await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
                    }
                    else
                    {
                        var ids       = guids.Select(x => ConvertToSagaId(session, x)).ToArray();
                        var instances = await session.LoadAsync <TSaga>(ids);

                        foreach (var instance in instances)
                        {
                            await SendToInstance(context, policy, instance, next, session).ConfigureAwait(false);
                        }
                    }
                    await session.SaveChangesAsync();
                }
                catch (SagaException sex)
                {
                    if (_log.IsErrorEnabled)
                    {
                        _log.Error("Saga Exception Occurred", sex);
                    }

                    throw;
                }
                catch (Exception ex)
                {
                    if (_log.IsErrorEnabled)
                    {
                        _log.Error(
                            $"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}",
                            ex);
                    }

                    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
        {
            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, _requestOptions);

                    await policy.Missing(context, missingPipe).ConfigureAwait(false);
                }
                else
                {
                    foreach (var instance in sagaInstances)
                    {
                        // To support optimistic concurrency, we need the Document ETag
                        // So we will re-query to get the document, this should be fast because we are querying by Id
                        var response = await _client
                                       .ReadDocumentAsync(UriFactory.CreateDocumentUri(_databaseName, _collectionName, instance.CorrelationId.ToString()), _requestOptions)
                                       .ConfigureAwait(false);

                        await SendToInstance(context, policy, next, instance, response.Resource).ConfigureAwait(false);
                    }
                }
            }
            catch (SagaException sex)
            {
                if (_log.IsErrorEnabled)
                {
                    _log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", sex);
                }

                throw;
            }
            catch (Exception ex)
            {
                if (_log.IsErrorEnabled)
                {
                    _log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", ex);
                }

                throw new SagaException(ex.Message, typeof(TSaga), typeof(T), Guid.Empty, ex);
            }
        }
        public async Task Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next)
            where T : class
        {
            if (!context.CorrelationId.HasValue)
            {
                throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T));
            }

            Document document = null;

            if (policy.PreInsertInstance(context, out var instance))
            {
                document = await PreInsertSagaInstance(context, instance).ConfigureAwait(false);
            }

            if (instance == null)
            {
                try
                {
                    ResourceResponse <Document> response = await _client
                                                           .ReadDocumentAsync(UriFactory.CreateDocumentUri(_databaseName, _collectionName, context.CorrelationId.ToString()), _requestOptions)
                                                           .ConfigureAwait(false);

                    document = response.Resource;
                }
                catch (DocumentClientException e) when(e.StatusCode == System.Net.HttpStatusCode.NotFound)
                {
                    // Couldn't find the document, swallowing exception
                }

                if (document != null)
                {
                    instance = JsonConvert.DeserializeObject <TSaga>(document.ToString());
                }
            }

            if (instance == null)
            {
                var missingSagaPipe =
                    new MissingPipe <TSaga, T>(_client, _databaseName, _collectionName, next, _documentDbSagaConsumeContextFactory, _requestOptions);

                await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
            }
            else
            {
                await SendToInstance(context, policy, next, instance, document).ConfigureAwait(false);
            }
        }
        public async Task SendQuery <T>(SagaQueryConsumeContext <TSaga, T> context, ISagaPolicy <TSaga, T> policy,
                                        IPipe <SagaConsumeContext <TSaga, T> > next) where T : class
        {
            using (var dbContext = _sagaDbContextFactory())
                using (var transaction = dbContext.Database.BeginTransaction(_isolationLevel))
                {
                    try
                    {
                        var sagaInstances = await dbContext.Set <TSaga>().Where(context.Query.FilterExpression).ToListAsync().ConfigureAwait(false);

                        if (sagaInstances.Count == 0)
                        {
                            var missingSagaPipe = new MissingPipe <T>(dbContext, next);

                            await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
                        }
                        else
                        {
                            foreach (var instance in sagaInstances)
                            {
                                await SendToInstance(context, dbContext, policy, instance, next).ConfigureAwait(false);
                            }
                        }

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

                        transaction.Commit();
                    }
                    catch (SagaException sex)
                    {
                        transaction.Rollback();
                        if (_log.IsErrorEnabled)
                        {
                            _log.Error("Saga Exception Occurred", sex);
                        }
                    }
                    catch (Exception ex)
                    {
                        transaction.Rollback();
                        if (_log.IsErrorEnabled)
                        {
                            _log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", ex);
                        }

                        throw new SagaException(ex.Message, typeof(TSaga), typeof(T), Guid.Empty, ex);
                    }
                }
        }
        async Task ISagaRepository <TSaga> .Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next)
        {
            MessageSessionContext sessionContext;

            if (!context.TryGetPayload(out sessionContext))
            {
                throw new SagaException($"The session-based saga repository requires an active message session: {TypeMetadataCache<TSaga>.ShortName}",
                                        typeof(TSaga), typeof(T));
            }

            Guid sessionId;

            if (Guid.TryParse(sessionContext.SessionId, out sessionId))
            {
                context = new CorrelationIdConsumeContextProxy <T>(context, sessionId);
            }

            var saga = await ReadSagaState(sessionContext).ConfigureAwait(false);

            if (saga == null)
            {
                var missingSagaPipe = new MissingPipe <T>(next, WriteSagaState);

                await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
            }
            else
            {
                SagaConsumeContext <TSaga, T> sagaConsumeContext = new MessageSessionSagaConsumeContext <TSaga, T>(context, sessionContext, saga);

                if (_log.IsDebugEnabled)
                {
                    _log.DebugFormat("SAGA:{0}:{1} Existing {2}", TypeMetadataCache <TSaga> .ShortName, sessionContext.SessionId, TypeMetadataCache <T> .ShortName);
                }

                await policy.Existing(sagaConsumeContext, next).ConfigureAwait(false);

                if (!sagaConsumeContext.IsCompleted)
                {
                    await WriteSagaState(sessionContext, saga).ConfigureAwait(false);

                    if (_log.IsDebugEnabled)
                    {
                        _log.DebugFormat("SAGA:{0}:{1} Updated {2}", TypeMetadataCache <TSaga> .ShortName, sessionContext.SessionId,
                                         TypeMetadataCache <T> .ShortName);
                    }
                }
            }
        }
        public void GivenAMissingPipe_WhenSendingAndProxyIncomplete()
        {
            IMongoCollection<SimpleSaga> collection = SagaRepository.Instance.GetCollection<SimpleSaga>("sagas");
            _nextPipe = new Mock<IPipe<SagaConsumeContext<SimpleSaga, InitiateSimpleSaga>>>();
            _proxy = new Mock<SagaConsumeContext<SimpleSaga, InitiateSimpleSaga>>();
            _proxy.SetupGet(m => m.IsCompleted).Returns(false);
            _consumeContextFactory = new Mock<IMongoDbSagaConsumeContextFactory>();
            _saga = new SimpleSaga {CorrelationId = Guid.NewGuid()};
            _context = new Mock<SagaConsumeContext<SimpleSaga, InitiateSimpleSaga>>();
            _context.SetupGet(m => m.Saga).Returns(_saga);
            _consumeContextFactory.Setup(m => m.Create(collection, _context.Object, _context.Object.Saga, false)).Returns(_proxy.Object);

            _pipe = new MissingPipe<SimpleSaga, InitiateSimpleSaga>(collection, _nextPipe.Object, _consumeContextFactory.Object);

            TaskUtil.Await(() => _pipe.Send(_context.Object));
        }
        public async Task GivenAMissingPipe_WhenSendingAndProxyIncomplete()
        {
            _nextPipe = new Mock <IPipe <SagaConsumeContext <SimpleSagaResource, InitiateSimpleSaga> > >();
            _proxy    = new Mock <SagaConsumeContext <SimpleSagaResource, InitiateSimpleSaga> >();
            _proxy.SetupGet(m => m.IsCompleted).Returns(false);
            _consumeContextFactory = new Mock <IDocumentDbSagaConsumeContextFactory>();
            _saga = new SimpleSagaResource {
                CorrelationId = Guid.NewGuid()
            };
            _context = new Mock <SagaConsumeContext <SimpleSagaResource, InitiateSimpleSaga> >();
            _context.SetupGet(m => m.Saga).Returns(_saga);
            _consumeContextFactory.Setup(m => m.Create(SagaRepository.Instance.Client, SagaRepository.DatabaseName, SagaRepository.CollectionName, _context.Object, It.IsAny <SimpleSagaResource>(), false, null)).Returns(_proxy.Object);

            _pipe = new MissingPipe <SimpleSagaResource, InitiateSimpleSaga>(SagaRepository.Instance.Client, SagaRepository.DatabaseName, SagaRepository.CollectionName, _nextPipe.Object, _consumeContextFactory.Object, null);

            await _pipe.Send(_context.Object);
        }
Esempio n. 26
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)
                {
                    if (_log.IsErrorEnabled)
                    {
                        _log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", sex);
                    }

                    throw;
                }
                catch (Exception ex)
                {
                    if (_log.IsErrorEnabled)
                    {
                        _log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", ex);
                    }

                    throw new SagaException(ex.Message, typeof(TSaga), typeof(T), Guid.Empty, ex);
                }
            }
        }
Esempio n. 27
0
        public async Task GivenAMissingPipe_WhenSendingAndProxyIncomplete()
        {
            var collection = SagaRepository.Instance.GetCollection <SimpleSaga>("sagas");

            _nextPipe = new Mock <IPipe <SagaConsumeContext <SimpleSaga, InitiateSimpleSaga> > >();
            _proxy    = new Mock <SagaConsumeContext <SimpleSaga, InitiateSimpleSaga> >();
            _proxy.SetupGet(m => m.IsCompleted).Returns(false);
            _consumeContextFactory = new Mock <IMongoDbSagaConsumeContextFactory>();
            _saga = new SimpleSaga {
                CorrelationId = Guid.NewGuid()
            };
            _context = new Mock <SagaConsumeContext <SimpleSaga, InitiateSimpleSaga> >();
            _context.SetupGet(m => m.Saga).Returns(_saga);
            _consumeContextFactory.Setup(m => m.Create(collection, _context.Object, _context.Object.Saga, false)).Returns(_proxy.Object);

            _pipe = new MissingPipe <SimpleSaga, InitiateSimpleSaga>(collection, _nextPipe.Object, _consumeContextFactory.Object);

            await _pipe.Send(_context.Object);
        }
        public async Task SendQuery <T>(SagaQueryConsumeContext <TSaga, T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next)
            where T : class
        {
            try
            {
                var 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)
            {
                if (_log.IsErrorEnabled)
                {
                    _log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", sex);
                }

                throw;
            }
            catch (Exception ex)
            {
                if (_log.IsErrorEnabled)
                {
                    _log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", ex);
                }

                throw new SagaException(ex.Message, typeof(TSaga), typeof(T), Guid.Empty, ex);
            }
        }
        public async Task Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy,
                                   IPipe <SagaConsumeContext <TSaga, T> > next) where T : class
        {
            if (!context.CorrelationId.HasValue)
            {
                throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T));
            }

            var   sagaId = context.CorrelationId.Value;
            var   db     = _redisConnection.GetDatabase();
            TSaga instance;
            ITypedDatabase <TSaga> sagas = db.As <TSaga>();

            if (policy.PreInsertInstance(context, out instance))
            {
                await PreInsertSagaInstance <T>(sagas, instance).ConfigureAwait(false);
            }

            if (instance == null)
            {
                using (var distLock = _lockFactory.Create($"redislock:{context.CorrelationId.Value}", _expiry, _wait, _retryTime))
                {
                    if (distLock.IsAcquired)
                    {
                        instance = await sagas.Get(sagaId, _redisPrefix).ConfigureAwait(false);
                    }
                }
            }



            if (instance == null)
            {
                var missingSagaPipe = new MissingPipe <T>(db, _lockFactory, next, _redisPrefix);
                await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
            }
            else
            {
                await SendToInstance(context, policy, next, instance).ConfigureAwait(false);
            }
        }
        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)
            {
                if (_log.IsErrorEnabled)
                {
                    _log.Error("Saga Exception Occurred", sex);
                }
            }
            catch (Exception ex)
            {
                if (_log.IsErrorEnabled)
                {
                    _log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", ex);
                }

                throw new SagaException(ex.Message, typeof(TSaga), typeof(T), Guid.Empty, ex);
            }
        }
Esempio n. 31
0
        public async Task Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy,
                                   IPipe <SagaConsumeContext <TSaga, T> > next) where T : class
        {
            if (!context.CorrelationId.HasValue)
            {
                throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T));
            }

            var   sagaId = context.CorrelationId.Value;
            TSaga instance;

            using (var redis = _clientsManager.GetClient())
            {
                var sagas = redis.As <TSaga>();

                if (policy.PreInsertInstance(context, out instance))
                {
                    await PreInsertSagaInstance <T>(sagas, instance).ConfigureAwait(false);
                }

                if (instance == null)
                {
                    instance = sagas.GetById(sagaId);
                }
            }

            if (instance == null)
            {
                var missingSagaPipe = new MissingPipe <T>(_clientsManager, next);
                await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
            }
            else
            {
                await SendToInstance(context, policy, next, instance).ConfigureAwait(false);
            }
        }
        async Task ISagaRepository <TSaga> .Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy,
                                                     IPipe <SagaConsumeContext <TSaga, T> > next)
        {
            if (!context.CorrelationId.HasValue)
            {
                throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T));
            }

            var sagaId = context.CorrelationId.Value;

            using (var dbContext = _sagaDbContextFactory())
                using (var transaction = dbContext.Database.BeginTransaction(_isolationLevel))
                {
                    // Hack for locking row for the duration of the transaction.
                    var tableName = ((IObjectContextAdapter)dbContext).ObjectContext.CreateObjectSet <TSaga>().EntitySet.Name;
                    await dbContext.Database.ExecuteSqlCommandAsync($"select 1 from {tableName} WITH (UPDLOCK, ROWLOCK) WHERE CorrelationId = @p0", sagaId)
                    .ConfigureAwait(false);

                    var inserted = false;

                    TSaga instance;
                    if (policy.PreInsertInstance(context, out instance))
                    {
                        inserted = await PreInsertSagaInstance <T>(dbContext, instance, context.CancellationToken).ConfigureAwait(false);
                    }

                    try
                    {
                        if (instance == null)
                        {
                            instance = dbContext.Set <TSaga>().SingleOrDefault(x => x.CorrelationId == sagaId);
                        }
                        if (instance == null)
                        {
                            var missingSagaPipe = new MissingPipe <T>(dbContext, next);

                            await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
                        }
                        else
                        {
                            if (_log.IsDebugEnabled)
                            {
                                _log.DebugFormat("SAGA:{0}:{1} Used {2}", TypeMetadataCache <TSaga> .ShortName, instance.CorrelationId,
                                                 TypeMetadataCache <T> .ShortName);
                            }

                            var sagaConsumeContext = new EntityFrameworkSagaConsumeContext <TSaga, T>(dbContext, context, instance);

                            await policy.Existing(sagaConsumeContext, next).ConfigureAwait(false);
                        }

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

                        transaction.Commit();
                    }
                    catch (DbUpdateException ex)
                    {
                        var baseException = ex.GetBaseException() as SqlException;
                        if (baseException != null && baseException.Number == 1205)
                        {
                            // deadlock, no need to rollback
                        }
                        else
                        {
                            try
                            {
                                transaction.Rollback();
                            }
                            catch (Exception innerException)
                            {
                                if (_log.IsWarnEnabled)
                                {
                                    _log.Warn("The transaction rollback failed", innerException);
                                }
                            }
                        }

                        throw;
                    }
                    catch (Exception)
                    {
                        try
                        {
                            transaction.Rollback();
                        }
                        catch (Exception innerException)
                        {
                            if (_log.IsWarnEnabled)
                            {
                                _log.Warn("The transaction rollback failed", innerException);
                            }
                        }
                        throw;
                    }
                }
        }
        public async Task SendQuery <T>(SagaQueryConsumeContext <TSaga, T> context, ISagaPolicy <TSaga, T> policy,
                                        IPipe <SagaConsumeContext <TSaga, T> > next) where T : class
        {
            using (var dbContext = _sagaDbContextFactory())
            {
                // 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.
                var correlationIds = await dbContext.Set <TSaga>().Where(context.Query.FilterExpression)
                                     .Select(x => x.CorrelationId)
                                     .ToListAsync()
                                     .ConfigureAwait(false);

                using (var transaction = dbContext.Database.BeginTransaction(_isolationLevel))
                {
                    try
                    {
                        if (correlationIds.Count == 0)
                        {
                            var missingSagaPipe = new MissingPipe <T>(dbContext, next);

                            await policy.Missing(context, missingSagaPipe).ConfigureAwait(false);
                        }
                        else
                        {
                            var tableName = ((IObjectContextAdapter)dbContext).ObjectContext.CreateObjectSet <TSaga>().EntitySet.Name;
                            foreach (var correlationId in correlationIds)
                            {
                                // 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(
                                    $"select 2 from {tableName} WITH (UPDLOCK, ROWLOCK) WHERE CorrelationId = @p0",
                                    correlationId).ConfigureAwait(false);

                                var instance = dbContext.Set <TSaga>().SingleOrDefault(x => x.CorrelationId == correlationId);

                                await SendToInstance(context, dbContext, policy, instance, next).ConfigureAwait(false);
                            }
                        }

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

                        transaction.Commit();
                    }
                    catch (DbUpdateException ex)
                    {
                        var baseException = ex.GetBaseException() as SqlException;
                        if (baseException != null && baseException.Number == 1205)
                        {
                            // deadlock, no need to rollback
                        }
                        else
                        {
                            try
                            {
                                transaction.Rollback();
                            }
                            catch (Exception innerException)
                            {
                                if (_log.IsWarnEnabled)
                                {
                                    _log.Warn("The transaction rollback failed", innerException);
                                }
                            }
                        }

                        throw;
                    }
                    catch (SagaException sex)
                    {
                        try
                        {
                            transaction.Rollback();
                        }
                        catch (Exception innerException)
                        {
                            if (_log.IsWarnEnabled)
                            {
                                _log.Warn("The transaction rollback failed", innerException);
                            }
                        }

                        if (_log.IsErrorEnabled)
                        {
                            _log.Error("Saga Exception Occurred", sex);
                        }
                    }
                    catch (Exception ex)
                    {
                        try
                        {
                            transaction.Rollback();
                        }
                        catch (Exception innerException)
                        {
                            if (_log.IsWarnEnabled)
                            {
                                _log.Warn("The transaction rollback failed", innerException);
                            }
                        }

                        if (_log.IsErrorEnabled)
                        {
                            _log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", ex);
                        }

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