protected static void SendMessageToNewSaga <TMessage>(ISagaPolicy <TSaga, TMessage> policy, TMessage message, Action <TSaga> consumerAction, Action <TSaga> removeAction) { TSaga saga; if (!policy.CreateSagaWhenMissing(message, out saga)) { return; } if (_log.IsDebugEnabled) { _log.DebugFormat("Created saga [{0}] - {1}", typeof(TSaga).ToFriendlyName(), saga.CorrelationId); } try { lock (saga) consumerAction(saga); if (policy.ShouldSagaBeRemoved(saga)) { removeAction(saga); } } catch (Exception ex) { var sex = new SagaException("Saga consumer exception", typeof(TSaga), typeof(TMessage), saga.CorrelationId, ex); if (_log.IsErrorEnabled) { _log.Error("Existing Saga Exception: ", sex); } throw sex; } }
public Task Send(ConsumeContext <TData> context) { using (RetryPolicyContext <ConsumeContext <TData> > policyContext = _retryPolicy.CreatePolicyContext(context)) { var exception = new SagaException("An existing saga instance was not found", typeof(TInstance), typeof(TData), context.CorrelationId ?? Guid .Empty); if (!policyContext.CanRetry(exception, out RetryContext <ConsumeContext <TData> > retryContext)) { return(_finalPipe.Send(context)); } int previousDeliveryCount = context.GetRedeliveryCount(); for (int retryIndex = 0; retryIndex < previousDeliveryCount; retryIndex++) { if (!retryContext.CanRetry(exception, out retryContext)) { return(_finalPipe.Send(context)); } } var schedulerContext = context.GetPayload <MessageSchedulerContext>(); MessageRedeliveryContext redeliveryContext = new ScheduleMessageRedeliveryContext <TData>(context, schedulerContext); var delay = retryContext.Delay ?? TimeSpan.Zero; return(redeliveryContext.ScheduleRedelivery(delay)); } }
/// <summary> /// Creates new saga action. /// </summary> /// <exception cref="SagaException"> Thrown when a saga error condition occurs.</exception> /// <typeparam name="TMessage"> Type of the message.</typeparam> /// <param name="sagaId"> Identifier for the saga.</param> /// <param name="selector"> The selector.</param> /// <param name="policy"> The policy.</param> /// <returns> /// The new new saga action< t message> /// </returns> Action <IConsumeContext <TMessage> > CreateNewSagaAction <TMessage>( Guid sagaId, InstanceHandlerSelector <TSaga, TMessage> selector, ISagaPolicy <TSaga, TMessage> policy) where TMessage : class { return(x => { if (Log.IsDebugEnabled) { Log.DebugFormat( "SAGA: {0} Creating New {1} for {2}", typeof(TSaga).ToFriendlyName(), sagaId, typeof(TMessage).ToFriendlyName()); } try { TSaga instance = policy.CreateInstance(x, sagaId); foreach (var callback in selector(instance, x)) { callback(x); } if (!policy.CanRemoveInstance(instance)) { Collection.Insert(instance, WriteConcern.Acknowledged); } else { Collection.Save(instance, WriteConcern.Acknowledged); } } catch (Exception ex) { var sagaException = new SagaException( "Create Saga Instance Exception", typeof(TSaga), typeof(TMessage), sagaId, ex); if (Log.IsErrorEnabled) { Log.Error(sagaException); } throw sagaException; } }); }
/// <summary> /// Use existing saga action. /// </summary> /// <exception cref="SagaException"> Thrown when a saga error condition occurs.</exception> /// <typeparam name="TMessage"> Type of the message.</typeparam> /// <param name="sagaId"> Identifier for the saga.</param> /// <param name="selector"> The selector.</param> /// <param name="policy"> The policy.</param> /// <param name="instance"> The instance.</param> /// <returns> /// . /// </returns> Action <IConsumeContext <TMessage> > UseExistingSagaAction <TMessage>( Guid sagaId, InstanceHandlerSelector <TSaga, TMessage> selector, ISagaPolicy <TSaga, TMessage> policy, TSaga instance) where TMessage : class { return(x => { if (Log.IsDebugEnabled) { Log.DebugFormat( "SAGA: {0} Using Existing {1} for {2}", typeof(TSaga).ToFriendlyName(), sagaId, typeof(TMessage).ToFriendlyName()); } try { foreach (var callback in selector(instance, x)) { callback(x); } if (policy.CanRemoveInstance(instance)) { Collection.Remove( GetMongoQuery(Queryable.Where(q => q.CorrelationId == sagaId)), RemoveFlags.Single, WriteConcern.Acknowledged); } } catch (Exception ex) { var sagaException = new SagaException( "Existing Saga Instance Exception", typeof(TSaga), typeof(TMessage), sagaId, ex); if (Log.IsErrorEnabled) { Log.Error(sagaException); } throw sagaException; } }); }
protected static bool SendMessageToExistingSagas <TMessage>(IEnumerable <TSaga> existingSagas, ISagaPolicy <TSaga, TMessage> policy, Action <TSaga> consumerAction, TMessage message, Action <TSaga> removeAction) { int sagaCount = 0; Exception lastException = null; foreach (TSaga saga in existingSagas) { try { sagaCount++; if (_log.IsDebugEnabled) { _log.DebugFormat("Found saga [{0}] - {1}", typeof(TSaga).ToFriendlyName(), saga.CorrelationId); } policy.ForExistingSaga(message); lock (saga) consumerAction(saga); if (policy.ShouldSagaBeRemoved(saga)) { removeAction(saga); } } catch (Exception ex) { var sex = new SagaException("Saga consumer exception", typeof(TSaga), typeof(TMessage), saga.CorrelationId, ex); if (_log.IsErrorEnabled) { _log.Error("Existing Saga Exception: ", sex); } lastException = sex; } } if (lastException != null) { throw lastException; } return(sagaCount > 0); }
public async Task GivenAMongoDbSagaRepository_WhenSendingWithNullCorrelationId() { var context = new Mock<ConsumeContext<InitiateSimpleSaga>>(); context.Setup(x => x.CorrelationId).Returns(default(Guid?)); var repository = new MongoDbSagaRepository<SimpleSaga>(Mock.Of<IMongoDatabase>(), null); try { await repository.Send(context.Object, Mock.Of<ISagaPolicy<SimpleSaga, InitiateSimpleSaga>>(), Mock.Of<IPipe<SagaConsumeContext<SimpleSaga, InitiateSimpleSaga>>>()); } catch (SagaException exception) { _exception = exception; } }
public async Task GivenAMongoDbSagaRepository_WhenSendingWithNullCorrelationId() { var context = new Mock <ConsumeContext <InitiateSimpleSaga> >(); context.Setup(x => x.CorrelationId).Returns(default(Guid?)); var repository = new MongoDbSagaRepository <SimpleSaga>(Mock.Of <IMongoDatabase>(), null); try { await repository.Send(context.Object, Mock.Of <ISagaPolicy <SimpleSaga, InitiateSimpleSaga> >(), Mock.Of <IPipe <SagaConsumeContext <SimpleSaga, InitiateSimpleSaga> > >()); } catch (SagaException exception) { _exception = exception; } }
public async Task GivenADocumentDbSagaRepository_WhenSendingWithNullCorrelationId() { var context = new Mock <ConsumeContext <InitiateSimpleSaga> >(); context.Setup(x => x.CorrelationId).Returns(default(Guid?)); var repository = new DocumentDbSagaRepository <SimpleSagaResource>(Mock.Of <IDocumentClient>(), "sagaTest"); try { await repository.Send(context.Object, Mock.Of <ISagaPolicy <SimpleSagaResource, InitiateSimpleSaga> >(), Mock.Of <IPipe <SagaConsumeContext <SimpleSagaResource, InitiateSimpleSaga> > >()); } catch (SagaException exception) { _exception = exception; } }
public IEnumerable <Action <IConsumeContext <TMessage> > > GetSaga <TMessage>(IConsumeContext <TMessage> context, Guid sagaId, InstanceHandlerSelector <TSaga, TMessage> selector, ISagaPolicy <TSaga, TMessage> policy) where TMessage : class { using (ISession session = _sessionFactory.OpenSession()) using (ITransaction transaction = session.BeginTransaction()) { var instance = session.Get <TSaga>(sagaId, LockMode.Upgrade); if (instance == null) { if (policy.CanCreateInstance(context)) { yield return(x => { if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA: {0} Creating New {1} for {2}", typeof(TSaga).ToFriendlyName(), sagaId, typeof(TMessage).ToFriendlyName()); } try { instance = policy.CreateInstance(x, sagaId); foreach (var callback in selector(instance, x)) { callback(x); } if (!policy.CanRemoveInstance(instance)) { session.Save(instance); } } catch (Exception ex) { var sex = new SagaException("Create Saga Instance Exception", typeof(TSaga), typeof(TMessage), sagaId, ex); if (_log.IsErrorEnabled) { _log.Error(sex); } if (transaction.IsActive) { transaction.Rollback(); } throw sex; } }); } else { if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA: {0} Ignoring Missing {1} for {2}", typeof(TSaga).ToFriendlyName(), sagaId, typeof(TMessage).ToFriendlyName()); } } } else { if (policy.CanUseExistingInstance(context)) { yield return(x => { if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA: {0} Using Existing {1} for {2}", typeof(TSaga).ToFriendlyName(), sagaId, typeof(TMessage).ToFriendlyName()); } try { foreach (var callback in selector(instance, x)) { callback(x); } if (policy.CanRemoveInstance(instance)) { session.Delete(instance); } } catch (Exception ex) { var sex = new SagaException("Existing Saga Instance Exception", typeof(TSaga), typeof(TMessage), sagaId, ex); if (_log.IsErrorEnabled) { _log.Error(sex); } if (transaction.IsActive) { transaction.Rollback(); } throw sex; } }); } else { if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA: {0} Ignoring Existing {1} for {2}", typeof(TSaga).ToFriendlyName(), sagaId, typeof(TMessage).ToFriendlyName()); } } } if (transaction.IsActive) { transaction.Commit(); } } }
public IEnumerable <Action <IConsumeContext <TMessage> > > GetSaga <TMessage>(IConsumeContext <TMessage> context, Guid sagaId, InstanceHandlerSelector <TSaga, TMessage> selector, ISagaPolicy <TSaga, TMessage> policy) where TMessage : class { bool needToLeaveSagas = true; Monitor.Enter(_sagas); try { TSaga instance = _sagas[sagaId]; if (instance == null) { if (policy.CanCreateInstance(context)) { instance = policy.CreateInstance(context, sagaId); _sagas.Add(instance); lock (instance) { Monitor.Exit(_sagas); needToLeaveSagas = false; yield return(x => { if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA: {0} Creating New {1} for {2}", typeof(TSaga).ToFriendlyName(), sagaId, typeof(TMessage).ToFriendlyName()); } try { foreach (var callback in selector(instance, x)) { callback(x); } if (policy.CanRemoveInstance(instance)) { _sagas.Remove(instance); } } catch (Exception ex) { var sex = new SagaException("Create Saga Instance Exception", typeof(TSaga), typeof(TMessage), sagaId, ex); if (_log.IsErrorEnabled) { _log.Error(sex); } throw sex; } }); } } else { if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA: {0} Ignoring Missing {1} for {2}", typeof(TSaga).ToFriendlyName(), sagaId, typeof(TMessage).ToFriendlyName()); } } } else { if (policy.CanUseExistingInstance(context)) { Monitor.Exit(_sagas); needToLeaveSagas = false; lock (instance) { yield return(x => { if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA: {0} Using Existing {1} for {2}", typeof(TSaga).ToFriendlyName(), sagaId, typeof(TMessage).ToFriendlyName()); } try { foreach (var callback in selector(instance, x)) { callback(x); } if (policy.CanRemoveInstance(instance)) { _sagas.Remove(instance); } } catch (Exception ex) { var sex = new SagaException("Existing Saga Instance Exception", typeof(TSaga), typeof(TMessage), sagaId, ex); if (_log.IsErrorEnabled) { _log.Error(sex); } throw sex; } }); } } else { if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA: {0} Ignoring Existing {1} for {2}", typeof(TSaga).ToFriendlyName(), sagaId, typeof(TMessage).ToFriendlyName()); } } } } finally { if (needToLeaveSagas) { Monitor.Exit(_sagas); } } }
public IEnumerable <Action <IConsumeContext <TMessage> > > GetSaga <TMessage>( IConsumeContext <TMessage> context, Guid sagaId, InstanceHandlerSelector <TSaga, TMessage> selector, ISagaPolicy <TSaga, TMessage> policy) where TMessage : class { using (var session = this.documentStore.OpenSession()) { session.Advanced.UseOptimisticConcurrency = true; var instance = session.Load <TSaga>(sagaId); if (instance == null) { if (policy.CanCreateInstance(context)) { yield return(x => { try { instance = policy.CreateInstance(x, sagaId); foreach (var callback in selector(instance, x)) { callback(x); } if (policy.CanRemoveInstance(instance)) { return; } session.Store(instance); session.SaveChanges(); } catch (Exception ex) { var sex = new SagaException( "Create Saga Instance Exception", typeof(TSaga), typeof(TMessage), sagaId, ex); throw sex; } }); } } else { if (policy.CanUseExistingInstance(context)) { yield return(x => { try { const int NbtriesMax = 5; foreach (var callback in selector(instance, x)) { callback(x); } if (policy.CanRemoveInstance(instance)) { session.Delete(instance); } try { session.SaveChanges(); } catch (ConcurrencyException) { if (x.RetryCount <= NbtriesMax) { x.RetryLater(); } else { throw; } } } catch (Exception ex) { var sex = new SagaException( "Existing Saga Instance Exception", typeof(TSaga), typeof(TMessage), sagaId, ex); throw sex; } }); } } } }
public IEnumerable <Action <IConsumeContext <TMessage> > > GetSaga <TMessage>(IConsumeContext <TMessage> context, Guid sagaId, InstanceHandlerSelector <TSaga, TMessage> selector, ISagaPolicy <TSaga, TMessage> policy) where TMessage : class { using (var db = dbProvider.Open()) using (var transaction = db.BeginTransaction()) { TSaga instance = db.Get <TSaga>(sagaId, transaction); Console.WriteLine("Got instance: {0}", instance == null ? "no instance" : instance.ToString()); if (instance == null) { if (policy.CanCreateInstance(context)) { yield return(x => { if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA: {0} Creating New {1} for {2}", typeof(TSaga).ToFriendlyName(), sagaId, typeof(TMessage).ToFriendlyName()); } try { instance = policy.CreateInstance(x, sagaId); foreach (var callback in selector(instance, x)) { callback(x); } if (!policy.CanRemoveInstance(instance)) { try { db.Insert(instance, transaction); } catch (Exception) { // if insert fails update db.Update(instance, transaction); } } } catch (Exception ex) { var sex = new SagaException("Create Saga Instance Exception", typeof(TSaga), typeof(TMessage), sagaId, ex); if (_log.IsErrorEnabled) { _log.Error(sex); } //if(transaction.IsActive) - Transaction is always active transaction.Rollback(); throw sex; } }); } else { if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA: {0} Ignoring Missing {1} for {2}", typeof(TSaga).ToFriendlyName(), sagaId, typeof(TMessage).ToFriendlyName()); } } } else { Console.WriteLine("Have existing instance " + instance.ToString()); if (policy.CanUseExistingInstance(context)) { yield return(x => { if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA: {0} Using Existing {1} for {2}", typeof(TSaga).ToFriendlyName(), sagaId, typeof(TMessage).ToFriendlyName()); } try { foreach (var callback in selector(instance, x)) { callback(x); } if (policy.CanRemoveInstance(instance)) { db.Delete(instance, transaction); } else { db.Update(instance, transaction); } } catch (Exception ex) { var sex = new SagaException("Existing Saga Instance Exception", typeof(TSaga), typeof(TMessage), sagaId, ex); if (_log.IsErrorEnabled) { _log.Error(sex); } //if(transaction.IsActive) - Transaction is always active transaction.Rollback(); throw sex; } }); } else { if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA: {0} Ignoring Existing {1} for {2}", typeof(TSaga).ToFriendlyName(), sagaId, typeof(TMessage).ToFriendlyName()); } } } transaction.Commit(); } }
public IEnumerable <Action <IConsumeContext <TMessage> > > GetSaga <TMessage>( IConsumeContext <TMessage> context, Guid instanceId, InstanceHandlerSelector <TInstance, TMessage> selector, ISagaPolicy <TInstance, TMessage> policy) where TMessage : class { ////using (var transaction = new TransactionScope(TransactionScopeOption.RequiresNew)) ////TODO: Looks like Custom json deserialization is not performed when GetDocument is executed ////var getResult = bucket.GetDocument<TInstance>(instanceId.ToString()); ////if (!getResult.Success) ////{ //// var sagaException = new SagaException( //// getResult.Message, //// typeof(TInstance), //// typeof(TMessage), //// instanceId, //// getResult.Exception); //// this.log.Error(sagaException); //// throw sagaException; ////} ////else if ( //// getResult.Document != null //// && getResult.Document.Content != null) ////{ //// instance = getResult.Document.Content; ////} var instance = this .Where(s => s.CorrelationId.Equals(instanceId)) .FirstOrDefault(); if (instance == null) { if (policy.CanCreateInstance(context)) { yield return(action => { this.log.DebugFormat( "{0} Creating New instance {1} for {2}", typeof(TInstance).ToFriendlyName(), instanceId, typeof(TMessage).ToFriendlyName()); try { instance = policy.CreateInstance(action, instanceId); foreach (var callback in selector(instance, action)) { callback(action); } if (!policy.CanRemoveInstance(instance)) { this.RetrieveAndProcessResult( () => this.bucket.Insert(instanceId.ToString(), instance), result => { }); } } catch (Exception exception) { var sagaException = new SagaException( "Create Saga Instance Exception", typeof(TInstance), typeof(TMessage), instanceId, exception); this.log.Error(sagaException); throw sagaException; } }); } else { this.log.DebugFormat( "{0} Ignoring Missing instance {1} for {2}", typeof(TInstance).ToFriendlyName(), instanceId, typeof(TMessage).ToFriendlyName()); } } else { if (policy.CanUseExistingInstance(context)) { yield return(action => { this.log.DebugFormat( "{0} Using Existing instance {1} for {2}", typeof(TInstance).ToFriendlyName(), instanceId, typeof(TMessage).ToFriendlyName()); try { foreach (var callback in selector(instance, action)) { callback(action); } if (policy.CanRemoveInstance(instance)) { this.RetrieveAndProcessResult( () => this.bucket.Remove(instanceId.ToString()), result => { }); } else { this.RetrieveAndProcessResult( () => this.bucket.Replace(instanceId.ToString(), instance), result => { }); } } catch (Exception exception) { var sagaException = new SagaException( "Existing Saga Instance Exception", typeof(TInstance), typeof(TMessage), instanceId, exception); this.log.Error(sagaException); throw sagaException; } }); } else { this.log.DebugFormat( "{0} Ignoring Existing instance {1} for {2}", typeof(TInstance).ToFriendlyName(), instanceId, typeof(TMessage).ToFriendlyName()); } } }