public async Task Send(SagaConsumeContext <TSaga, TMessage> context)
            {
                if (_log.IsDebugEnabled)
                {
                    _log.DebugFormat("SAGA:{0}:{1} Added {2}", TypeMetadataCache <TSaga> .ShortName,
                                     context.Saga.CorrelationId,
                                     TypeMetadataCache <TMessage> .ShortName);
                }

                SagaConsumeContext <TSaga, TMessage> proxy = new RedLockSagaConsumeContext <TSaga, TMessage>(_redisDb, _lockFactory, context, context.Saga, _redisPrefix);

                await _next.Send(proxy).ConfigureAwait(false);

                if (!proxy.IsCompleted)
                {
                    using (var distLock = _lockFactory.Create($"redislock:{context.Saga.CorrelationId}", _expiry, _wait, _retryTime))
                    {
                        if (distLock.IsAcquired)
                        {
                            // Work inside Lock
                            await _redisDb.As <TSaga>().Put(context.Saga.CorrelationId, context.Saga, _redisPrefix).ConfigureAwait(false);
                        }
                    }
                }
            }
예제 #2
0
 public static void LogFault <TSaga, TMessage>(this SagaConsumeContext <TSaga, TMessage> context, Exception exception, Guid?correlationId = default)
     where TSaga : class, ISaga
     where TMessage : class
 {
     LogContext.Error?.Log(exception, "SAGA:{SagaType}:{CorrelationId} Fault {MessageType}", TypeMetadataCache <TSaga> .ShortName, correlationId,
                           TypeMetadataCache <TMessage> .ShortName);
 }
예제 #3
0
        public async Task Send(SagaConsumeContext <TInstance, TData> context, IPipe <SagaConsumeContext <TInstance, TData> > next)
        {
            var eventContext = new StateMachineEventContextProxy <TInstance, TData>(context, _machine, context.Saga, _event, context.Message);

            var activity = LogContext.IfEnabled(OperationName.Saga.RaiseEvent)
                           ?.StartSagaActivity(context, (await _machine.Accessor.Get(eventContext).ConfigureAwait(false)).Name);

            try
            {
                await _machine.RaiseEvent(eventContext).ConfigureAwait(false);

                if (await _machine.IsCompleted(context.Saga).ConfigureAwait(false))
                {
                    await context.SetCompleted().ConfigureAwait(false);
                }
            }
            catch (UnhandledEventException ex)
            {
                var currentState = await _machine.Accessor.Get(eventContext).ConfigureAwait(false);

                throw new NotAcceptedStateMachineException(typeof(TInstance), typeof(TData), context.CorrelationId ?? Guid.Empty, currentState.Name, ex);
            }
            finally
            {
                activity?.AddTag(DiagnosticHeaders.EndState, (await _machine.Accessor.Get(eventContext).ConfigureAwait(false)).Name);
                activity?.Stop();
            }
        }
예제 #4
0
        public Task Send(ConsumeContext <TMessage> context)
        {
            SagaConsumeContext <TSaga, TMessage> sagaContext = context as SagaConsumeContext <TSaga, TMessage>
                                                               ?? new SagaConsumeContextProxy <TSaga, TMessage>(context, _context);

            return(_output.Send(sagaContext));
        }
예제 #5
0
 public static void LogUsed <TSaga, TMessage>(this SagaConsumeContext <TSaga, TMessage> context, Guid?correlationId = default)
     where TSaga : class, ISaga
     where TMessage : class
 {
     LogContext.Debug?.Log("SAGA:{SagaType}:{CorrelationId} Used {MessageType}", TypeMetadataCache <TSaga> .ShortName,
                           context.CorrelationId ?? correlationId, TypeMetadataCache <TMessage> .ShortName);
 }
        public async Task Send(SagaConsumeContext <TInstance, TData> context, IPipe <SagaConsumeContext <TInstance, TData> > next)
        {
            var eventContext = new StateMachineEventContext <TInstance, TData>(_machine, context.Saga, _event, context.Message, context.CancellationToken);

            eventContext.GetOrAddPayload(() => context);
            eventContext.GetOrAddPayload(() => (ConsumeContext <TData>)context);
            eventContext.GetOrAddPayload(() => (ConsumeContext)context);

            State <TInstance> currentState = await _machine.Accessor.Get(eventContext).ConfigureAwait(false);

            IEnumerable <Event> nextEvents = _machine.NextEvents(currentState);

            if (nextEvents.Contains(_event))
            {
                await _machine.RaiseEvent(eventContext).ConfigureAwait(false);

                if (_machine.IsCompleted(context.Saga))
                {
                    await context.SetCompleted().ConfigureAwait(false);
                }
            }
            else
            {
                throw new NotAcceptedStateMachineException(typeof(TInstance), typeof(TData), context.CorrelationId ?? Guid.Empty, currentState.Name);
            }
        }
예제 #7
0
        async Task SendToInstance <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next, TSaga instance)
            where T : class
        {
            try
            {
                SagaConsumeContext <TSaga, T> sagaConsumeContext =
                    _documentDbSagaConsumeContextFactory.Create(_client, _databaseName, _collectionName, context, instance, true, _requestOptions);

                sagaConsumeContext.LogUsed();

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

                if (!sagaConsumeContext.IsCompleted)
                {
                    await UpdateDocumentDbSaga(context, instance).ConfigureAwait(false);
                }
            }
            catch (SagaException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new SagaException(ex.Message, typeof(TSaga), typeof(T), instance.CorrelationId, ex);
            }
        }
        public void GivenAMongoDbSagaConsumeContext_WhenPoppingContext()
        {
            var mongoDbSagaConsumeContext = new MongoDbSagaConsumeContext <SimpleSaga, InitiateSimpleSaga>(Mock.Of <IMongoCollection <SimpleSaga> >(),
                                                                                                           Mock.Of <ConsumeContext <InitiateSimpleSaga> >(), Mock.Of <SimpleSaga>());

            _context = mongoDbSagaConsumeContext.PopContext <InitiateSimpleSaga>();
        }
예제 #9
0
        async Task SendToInstance <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next, TSaga instance)
            where T : class
        {
            try
            {
                SagaConsumeContext <TSaga, T> sagaConsumeContext = _mongoDbSagaConsumeContextFactory.Create(_collection, context, instance);

                sagaConsumeContext.LogUsed();

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

                if (!sagaConsumeContext.IsCompleted)
                {
                    await UpdateMongoDbSaga(context, instance).ConfigureAwait(false);
                }
            }
            catch (SagaException sex)
            {
                context.LogFault(this, sex, instance?.CorrelationId);

                throw;
            }
            catch (Exception ex)
            {
                throw new SagaException(ex.Message, typeof(TSaga), typeof(T), instance.CorrelationId, ex);
            }
        }
예제 #10
0
        public void GivenADocumentDbSagaConsumeContext_WhenPoppingContext()
        {
            var mongoDbSagaConsumeContext = new DocumentDbSagaConsumeContext <SimpleSaga, InitiateSimpleSaga>(It.IsAny <IDocumentClient>(), It.IsAny <string>(), It.IsAny <string>(),
                                                                                                              Mock.Of <ConsumeContext <InitiateSimpleSaga> >(), Mock.Of <SimpleSaga>());

            _context = mongoDbSagaConsumeContext.PopContext <InitiateSimpleSaga>();
        }
예제 #11
0
        public async Task Send(SagaRepositoryQueryContext <TSaga, T> context)
        {
            if (context.Count > 0)
            {
                async Task LoadInstance(Guid correlationId)
                {
                    SagaConsumeContext <TSaga, T> sagaConsumeContext = await context.Load(correlationId).ConfigureAwait(false);

                    if (sagaConsumeContext != null)
                    {
                        sagaConsumeContext.LogUsed();

                        try
                        {
                            await _policy.Existing(sagaConsumeContext, _next).ConfigureAwait(false);

                            if (_policy.IsReadOnly)
                            {
                                await context.Undo(sagaConsumeContext).ConfigureAwait(false);
                            }
                            else
                            {
                                if (sagaConsumeContext.IsCompleted)
                                {
                                    await context.Delete(sagaConsumeContext).ConfigureAwait(false);

                                    sagaConsumeContext.LogRemoved();
                                }
                                else
                                {
                                    await context.Update(sagaConsumeContext).ConfigureAwait(false);
                                }
                            }
                        }
                        finally
                        {
                            switch (sagaConsumeContext)
                            {
                            case IAsyncDisposable asyncDisposable:
                                await asyncDisposable.DisposeAsync().ConfigureAwait(false);

                                break;

                            case IDisposable disposable:
                                disposable.Dispose();
                                break;
                            }
                        }
                    }
                }

                await Task.WhenAll(context.Select(LoadInstance)).ConfigureAwait(false);
            }
            else
            {
                var missingPipe = new MissingSagaPipe <TSaga, T>(context, _next);

                await _policy.Missing(context, missingPipe).ConfigureAwait(false);
            }
        }
        public void GivenAMongoDbSagaConsumeContext_WhenPoppingContext()
        {
            var mongoDbSagaConsumeContext = new MongoDbSagaConsumeContext<SimpleSaga, InitiateSimpleSaga>(Mock.Of<IMongoCollection<SimpleSaga>>(),
                Mock.Of<ConsumeContext<InitiateSimpleSaga>>(), Mock.Of<SimpleSaga>());

            _context = mongoDbSagaConsumeContext.PopContext<InitiateSimpleSaga>();
        }
예제 #13
0
        public async Task Send(SagaConsumeContext <TSaga, TMessage> context)
        {
            SagaConsumeContext <TSaga, TMessage> sagaConsumeContext = await _repositoryContext.Add(context.Saga).ConfigureAwait(false);

            sagaConsumeContext.LogAdded();

            try
            {
                await _next.Send(sagaConsumeContext).ConfigureAwait(false);
            }
            finally
            {
                switch (sagaConsumeContext)
                {
                case IAsyncDisposable asyncDisposable:
                    await asyncDisposable.DisposeAsync().ConfigureAwait(false);

                    break;

                case IDisposable disposable:
                    disposable.Dispose();
                    break;
                }
            }
        }
            public async Task Send(SagaConsumeContext <TSaga, TMessage> context)
            {
                var instance = context.Saga;

                if (_logger.IsDebugEnabled)
                {
                    _logger.DebugFormat("SAGA: {0}:{1} Added {2}", TypeMetadataCache <TSaga> .ShortName,
                                        instance.CorrelationId,
                                        TypeMetadataCache <TMessage> .ShortName);
                }

                SagaConsumeContext <TSaga, TMessage> proxy =
                    new EventStoreSagaConsumeContext <TSaga, TMessage>(_connection, context, instance);

                await _next.Send(proxy).ConfigureAwait(false);

                if (!proxy.IsCompleted)
                {
                    await _connection.SaveEvents(
                        instance.StreamName,
                        instance.GetChanges(),
                        instance.ExpectedVersion,
                        new EventMetadata { CorrelationId = instance.CorrelationId, CausationId = context.MessageId });
                }
            }
        async Task SendToInstance <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next, TSaga instance)
            where T : class
        {
            try
            {
                if (_log.IsDebugEnabled)
                {
                    _log.DebugFormat("SAGA:{0}:{1} Used {2}", TypeMetadataCache <TSaga> .ShortName, instance.CorrelationId, TypeMetadataCache <T> .ShortName);
                }

                SagaConsumeContext <TSaga, T> sagaConsumeContext =
                    _documentDbSagaConsumeContextFactory.Create(_client, _databaseName, _collectionName, context, instance, true, _requestOptions);

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

                if (!sagaConsumeContext.IsCompleted)
                {
                    await UpdateDocumentDbSaga(context, instance).ConfigureAwait(false);
                }
            }
            catch (SagaException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new SagaException(ex.Message, typeof(TSaga), typeof(T), instance.CorrelationId, ex);
            }
        }
예제 #16
0
        public async Task Update <T>(SagaConsumeContext <TSaga, T> context)
            where T : class
        {
            var instance = context.Saga;

            IAsyncDisposable updateLock = _options.ConcurrencyMode == ConcurrencyMode.Optimistic
                ? updateLock = await Lock(instance, context.CancellationToken).ConfigureAwait(false)
                : null;

            try
            {
                instance.Version++;

                var existingInstance = await Get(instance.CorrelationId).ConfigureAwait(false);

                if (existingInstance.Version >= instance.Version)
                {
                    throw new RedisSagaConcurrencyException("Saga version conflict", typeof(TSaga), typeof(T), instance.CorrelationId);
                }

                await Put(instance).ConfigureAwait(false);
            }
            catch (Exception exception)
            {
                throw new SagaException("Saga update failed", typeof(TSaga), typeof(T), instance.CorrelationId, exception);
            }
            finally
            {
                if (updateLock != null)
                {
                    await updateLock.DisposeAsync(CancellationToken.None).ConfigureAwait(false);
                }
            }
        }
예제 #17
0
            public async Task Send(SagaConsumeContext <TSaga, TMessage> context)
            {
                var instance = new SagaInstance <TSaga>(context.Saga);

                await instance.MarkInUse(context.CancellationToken).ConfigureAwait(false);

                try
                {
                    var proxy = new InMemorySagaConsumeContext <TSaga, TMessage>(context, context.Saga, () => RemoveNewSaga(instance, context.CancellationToken));

                    if (_withinLock)
                    {
                        _repository.AddWithinLock(instance);
                    }
                    else
                    {
                        await _repository.Add(instance, context.CancellationToken).ConfigureAwait(false);
                    }

                    if (_log.IsDebugEnabled)
                    {
                        _log.DebugFormat("SAGA:{0}:{1} Added {2}", TypeMetadataCache <TSaga> .ShortName, context.Saga.CorrelationId,
                                         TypeMetadataCache <TMessage> .ShortName);
                    }

                    try
                    {
                        await _next.Send(proxy).ConfigureAwait(false);

                        if (proxy.IsCompleted)
                        {
                            if (_log.IsDebugEnabled)
                            {
                                _log.DebugFormat("SAGA:{0}:{1} Removed {2}", TypeMetadataCache <TSaga> .ShortName, context.Saga.CorrelationId,
                                                 TypeMetadataCache <TMessage> .ShortName);
                            }

                            await RemoveNewSaga(instance, context.CancellationToken).ConfigureAwait(false);
                        }
                    }
                    catch (Exception)
                    {
                        if (_log.IsDebugEnabled)
                        {
                            _log.DebugFormat("SAGA:{0}:{1} Removed(Fault) {2}", TypeMetadataCache <TSaga> .ShortName, context.Saga.CorrelationId,
                                             TypeMetadataCache <TMessage> .ShortName);
                        }

                        await RemoveNewSaga(instance, context.CancellationToken).ConfigureAwait(false);

                        throw;
                    }
                }
                finally
                {
                    instance.Release();
                }
            }
예제 #18
0
        public Task Send(SagaConsumeContext <TSaga> context)
        {
            if (context is SagaConsumeContext <TSaga, TMessage> consumerContext)
            {
                return(_output.Send(consumerContext));
            }

            throw new ArgumentException($"THe message could not be retrieved: {TypeMetadataCache<TMessage>.ShortName}", nameof(context));
        }
예제 #19
0
        public async Task Send(SagaRepositoryContext <TSaga, T> context)
        {
            SagaConsumeContext <TSaga, T> sagaConsumeContext = null;

            if (_policy.PreInsertInstance(context, out var instance))
            {
                sagaConsumeContext = await context.Insert(instance).ConfigureAwait(false);
            }

            sagaConsumeContext ??= await context.Load(_correlationId).ConfigureAwait(false);

            if (sagaConsumeContext != null)
            {
                try
                {
                    sagaConsumeContext.LogUsed();

                    await _policy.Existing(sagaConsumeContext, _next).ConfigureAwait(false);

                    if (_policy.IsReadOnly)
                    {
                        await context.Undo(sagaConsumeContext).ConfigureAwait(false);
                    }
                    else
                    {
                        if (sagaConsumeContext.IsCompleted)
                        {
                            await context.Delete(sagaConsumeContext).ConfigureAwait(false);

                            sagaConsumeContext.LogRemoved();
                        }
                        else
                        {
                            await context.Update(sagaConsumeContext).ConfigureAwait(false);
                        }
                    }
                }
                finally
                {
                    switch (sagaConsumeContext)
                    {
                    case IAsyncDisposable asyncDisposable:
                        await asyncDisposable.DisposeAsync().ConfigureAwait(false);

                        break;

                    case IDisposable disposable:
                        disposable.Dispose();
                        break;
                    }
                }
            }
            else
            {
                await _policy.Missing(context, new MissingSagaPipe <TSaga, T>(context, _next)).ConfigureAwait(false);
            }
        }
예제 #20
0
        public async Task GivenAMongoDbQuerySagaRepository_WhenFindingSaga()
        {
            _correlationId = Guid.NewGuid();

            await SagaRepository.InsertSaga(new SimpleSaga { CorrelationId = _correlationId });

            var repository = new MongoDbSagaRepositoryContext <SimpleSaga, InitiateSimpleSaga>(SagaRepository.Instance.GetCollection <SimpleSaga>("sagas"),
                                                                                               Mock.Of <ConsumeContext <InitiateSimpleSaga> >(), new MongoDbSagaConsumeContextFactory <SimpleSaga>());

            _result = await repository.Load(_correlationId);
        }
예제 #21
0
        public Task Send(ConsumeContext <TMessage> context)
        {
            if (ReferenceEquals(context, _context))
            {
                return(_output.Send(_context));
            }

            SagaConsumeContext <TSaga, TMessage> sagaContext = context as SagaConsumeContext <TSaga, TMessage>
                                                               ?? new SagaConsumeContextProxy <TSaga, TMessage>(context, _context);

            return(_output.Send(sagaContext));
        }
예제 #22
0
            public async Task Send(SagaConsumeContext <TSaga, TMessage> context)
            {
                var sagaConsumeContext = new DapperSagaConsumeContext <TSaga, TMessage>(_sqlConnection, context, context.Saga, _tableName, false);

                sagaConsumeContext.LogAdded();

                await _next.Send(sagaConsumeContext).ConfigureAwait(false);

                if (!sagaConsumeContext.IsCompleted)
                {
                    await _insertSagaInstance(_sqlConnection, context.Saga).ConfigureAwait(false);
                }
            }
예제 #23
0
        public async Task Send(SagaConsumeContext <TSaga, TMessage> context)
        {
            SagaConsumeContext <TSaga, TMessage> proxy = _mongoDbSagaConsumeContextFactory.Create(_collection, context, context.Saga, false);

            proxy.LogAdded();

            await _next.Send(proxy).ConfigureAwait(false);

            if (!proxy.IsCompleted)
            {
                await _collection.InsertOneAsync(context.Saga).ConfigureAwait(false);
            }
        }
예제 #24
0
            public async Task Send(SagaConsumeContext <TSaga, TMessage> context)
            {
                SagaConsumeContext <TSaga, TMessage> proxy = new RedisSagaConsumeContext <TSaga, TMessage>(_sagas, context, context.Saga);

                proxy.LogAdded();

                await _next.Send(proxy).ConfigureAwait(false);

                if (!proxy.IsCompleted)
                {
                    await _sagas.Put(context.Saga.CorrelationId, context.Saga).ConfigureAwait(false);
                }
            }
예제 #25
0
            public async Task Send(SagaConsumeContext <TSaga, TMessage> context)
            {
                context.LogAdded();

                SagaConsumeContext <TSaga, TMessage> proxy = new NHibernateSagaConsumeContext <TSaga, TMessage>(_session, context, context.Saga);

                await _next.Send(proxy).ConfigureAwait(false);

                if (!proxy.IsCompleted)
                {
                    await _session.SaveAsync(context.Saga).ConfigureAwait(false);
                }
            }
예제 #26
0
            public Task Send(SagaConsumeContext <TInstance, TMessage> context, IPipe <SagaConsumeContext <TInstance, TMessage> > next)
            {
                if (context.TryGetPayload(out TScope _))
                {
                    _taskCompletionSource.TrySetResult(context);
                }
                else
                {
                    _taskCompletionSource.TrySetException(new PayloadException("Service Provider not found"));
                }

                return(next.Send(context));
            }
예제 #27
0
            public async Task Send(SagaConsumeContext <TSaga, TMessage> context)
            {
                var instance = new SagaInstance <TSaga>(context.Saga);

                await instance.MarkInUse(context.CancellationToken).ConfigureAwait(false);

                var activity = LogContext.IfEnabled(OperationName.Saga.Add)?.StartActivity(new { context.Saga.CorrelationId });

                try
                {
                    var sagaConsumeContext = new InMemorySagaConsumeContext <TSaga, TMessage>(context, context.Saga,
                                                                                              () => RemoveNewSaga(instance, context.CancellationToken));

                    if (_withinLock)
                    {
                        _repository.AddWithinLock(instance);
                    }
                    else
                    {
                        await _repository.Add(instance, context.CancellationToken).ConfigureAwait(false);
                    }

                    sagaConsumeContext.LogAdded();

                    try
                    {
                        await _next.Send(sagaConsumeContext).ConfigureAwait(false);

                        if (sagaConsumeContext.IsCompleted)
                        {
                            await RemoveNewSaga(instance, context.CancellationToken).ConfigureAwait(false);

                            sagaConsumeContext.LogRemoved();
                        }
                    }
                    catch (Exception exception)
                    {
                        await RemoveNewSaga(instance, context.CancellationToken).ConfigureAwait(false);

                        sagaConsumeContext.LogRemoved(exception);

                        throw;
                    }
                }
                finally
                {
                    instance.Release();

                    activity?.Stop();
                }
            }
        public async Task Send(SagaConsumeContext<TSaga, TMessage> context, IPipe<SagaConsumeContext<TSaga, TMessage>> next)
        {
            var saga = context.Saga as InitiatedBy<TMessage>;
            if (saga == null)
            {
                string message = $"Saga type {TypeMetadataCache<TSaga>.ShortName} is not initiated by message type {TypeMetadataCache<TMessage>.ShortName}";

                throw new ConsumerMessageException(message);
            }

            await saga.Consume(context).ConfigureAwait(false);

            await next.Send(context).ConfigureAwait(false);
        }
예제 #29
0
            public async Task Send(SagaConsumeContext <TSaga, TMessage> context)
            {
                SagaConsumeContext <TSaga, TMessage> proxy = new MartenSagaConsumeContext <TSaga, TMessage>(_session, context, context.Saga);

                proxy.LogAdded();

                await _next.Send(proxy).ConfigureAwait(false);

                if (!proxy.IsCompleted)
                {
                    _session.Store(context.Saga);
                    await _session.SaveChangesAsync().ConfigureAwait(false);
                }
            }
예제 #30
0
        public async Task Add <T>(SagaConsumeContext <TSaga, T> context)
            where T : class
        {
            var instance = context.Saga;

            try
            {
                await Put(instance).ConfigureAwait(false);
            }
            catch (Exception exception)
            {
                throw new SagaException("Saga update failed", typeof(TSaga), typeof(T), instance.CorrelationId, exception);
            }
        }
예제 #31
0
        public async Task Send(SagaConsumeContext <TSaga, TMessage> context, IPipe <SagaConsumeContext <TSaga, TMessage> > next)
        {
            StartedActivity?activity = LogContext.IfEnabled(OperationName.Saga.InitiateOrOrchestrate)?.StartSagaActivity(context);

            try
            {
                await context.Saga.Consume(context).ConfigureAwait(false);

                await next.Send(context).ConfigureAwait(false);
            }
            finally
            {
                activity?.Stop();
            }
        }