Пример #1
0
        public Task Complete(IContainSagaData sagaData, SynchronizedStorageSession session, ContextBag context, CancellationToken cancellationToken = default)
        {
            var storageSession = (LearningSynchronizedStorageSession)session;

            storageSession.Complete(sagaData);
            return(Task.CompletedTask);
        }
Пример #2
0
        public async Task <TSagaData> Get <TSagaData>(Guid sagaId, SynchronizedStorageSession session, ContextBag context) where TSagaData : IContainSagaData
        {
            var sql = @"
SELECT 
    data
FROM 
    sagas
WHERE 
    id = @Id
            ";

            var storageSession = session.PostgreSqlSession();

            using (var command = new NpgsqlCommand(sql, storageSession.Connection, storageSession.Transaction))
            {
                command.Parameters.AddWithValue("Id", sagaId);

                using (var reader = await command.ExecuteReaderAsync(CommandBehavior.SingleRow))
                {
                    if (!await reader.ReadAsync())
                    {
                        return(default(TSagaData));
                    }

                    var json = reader.GetString(0);

                    TSagaData sagaData = JsonConvert.DeserializeObject <TSagaData>(json);
                    return(sagaData);
                }
            }
        }
        public async Task <TSagaData> Get <TSagaData>(Guid sagaId, SynchronizedStorageSession session, ContextBag context) where TSagaData : IContainSagaData
        {
            var operations      = new SagaPersisterAtomicOperations(session);
            var incomingMessage = context.Get <IncomingMessage>();
            var messageId       = incomingMessage.MessageId;
            var indexStream     = BuildSagaByIdStreamName(typeof(TSagaData), sagaId);
            var slice           = await session.ReadStreamEventsBackwardAsync(indexStream, -1, 1, true).ConfigureAwait(false);

            if (slice.Status != SliceReadStatus.Success)
            {
                return(default(TSagaData));
            }
            if (slice.Events[0].Event.EventType != SagaIndexEventType)
            {
                return(await operations.ReadSagaData <TSagaData>(indexStream, slice.Events[0], messageId));
            }
            var indexEntry = slice.Events[0].Event.Data.ParseJson <SagaIndexEvent>();
            var dataStream = indexEntry.DataStreamName;

            slice = await session.ReadStreamEventsBackwardAsync(dataStream, -1, 1, true).ConfigureAwait(false);

            if (slice.Status != SliceReadStatus.Success)
            {
                return(default(TSagaData));
            }
            var sagaData = await operations.ReadSagaData <TSagaData>(dataStream, slice.Events[0], messageId);

            // ReSharper disable once CompareNonConstrainedGenericWithNull
            if (sagaData != null)
            {
                return(sagaData);
            }
            throw new Exception($"Either the saga has not yet been created successfully or saga ID {sagaId} has been sent out in a ghost message.");
        }
        public TDbContext GetDataContext(SynchronizedStorageSession storageSession)
        {
            var dbConnection = storageSession.SqlPersistenceSession().Connection;

            if (_log.IsEnabled(LogEventLevel.Verbose))
            {
                _log.Verbose($"Creating {typeof(TDbContext).FullName} context");
            }

            var dbContext = DbContextThreadAsyncFactory.CreateDbContext(dbConnection, _schema, _builder.Build <TDbContext>);

            // Use the same underlying ADO.NET transaction
            dbContext.Database.UseTransaction(storageSession.SqlPersistenceSession().Transaction);

            // Call SaveChanges before completing storage session
            storageSession.SqlPersistenceSession().OnSaveChanges(x =>
            {
                if (_log.IsEnabled(LogEventLevel.Verbose))
                {
                    _log.Verbose($"Auto saving changes for {typeof(TDbContext).FullName} context");
                }

                return(dbContext.SaveChangesAsync());
            });

            return(dbContext);
        }
    internal async Task Save(IContainSagaData sagaData, SynchronizedStorageSession session, object correlationId)
    {
        var sqlSession = session.SqlPersistenceSession();
        var sagaInfo   = sagaInfoCache.GetInfo(sagaData.GetType());

        using (var command = sqlDialect.CreateCommand(sqlSession.Connection))
        {
            command.Transaction = sqlSession.Transaction;
            command.CommandText = sagaInfo.SaveCommand;
            command.AddParameter("Id", sagaData.Id);
            var metadata = new Dictionary <string, string>();
            if (sagaData.OriginalMessageId != null)
            {
                metadata.Add("OriginalMessageId", sagaData.OriginalMessageId);
            }
            if (sagaData.Originator != null)
            {
                metadata.Add("Originator", sagaData.Originator);
            }
            command.AddParameter("Metadata", Serializer.Serialize(metadata));
            command.AddJsonParameter("Data", sqlDialect.BuildSagaData(command, sagaInfo, sagaData));
            command.AddParameter("PersistenceVersion", StaticVersions.PersistenceVersion);
            command.AddParameter("SagaTypeVersion", sagaInfo.CurrentVersion);
            if (correlationId != null)
            {
                command.AddParameter("CorrelationId", correlationId);
            }
            AddTransitionalParameter(sagaData, sagaInfo, command);
            await command.ExecuteNonQueryEx().ConfigureAwait(false);
        }
    }
Пример #6
0
        public async Task <TSagaData> Get <TSagaData>(Guid sagaId, SynchronizedStorageSession session, ContextBag context) where TSagaData : IContainSagaData
        {
            var sagas = await GetSagasDictionary().ConfigureAwait(false);

            var secondaryIndex = await GetIndexDicitonary().ConfigureAwait(false);

            using (var tx = stateManager.CreateTransaction())
            {
                var correlationId = await secondaryIndex.TryGetValueAsync(tx, sagaId, LockMode.Default).ConfigureAwait(false);

                if (correlationId.HasValue == false)
                {
                    return(default(TSagaData));
                }

                var sagaData = await sagas.TryGetValueAsync(tx, correlationId.Value, LockMode.Default).ConfigureAwait(false);

                if (sagaData.HasValue == false)
                {
                    throw new Exception($"Failed to update saga data for SagaId='{sagaId}' and CorrelationId='{correlationId.Value}'");
                }

                return(Resolve <TSagaData>(sagaData.Value, context));
            }
        }
        public Task <T> Get <T>(Guid sagaId, SynchronizedStorageSession session, ContextBag context)
            where T : class, IContainSagaData
        {
            var documentSession = session.RavenSession();

            return(documentSession.LoadAsync <T>(sagaId));
        }
Пример #8
0
        public async Task Complete(IContainSagaData sagaData, SynchronizedStorageSession session, ContextBag context)
        {
            var sagas = await GetSagasDictionary().ConfigureAwait(false);

            var secondaryIndex = await GetIndexDicitonary().ConfigureAwait(false);

            using (var tx = stateManager.CreateTransaction())
            {
                var sagaId        = sagaData.Id;
                var correlationId = await GetCorrelationId(secondaryIndex, tx, sagaId).ConfigureAwait(false);

                await secondaryIndex.TryRemoveAsync(tx, sagaId).ConfigureAwait(false);

                var previouslyExistingState = await sagas.TryRemoveAsync(tx, correlationId).ConfigureAwait(false);

                if (previouslyExistingState.HasValue == false)
                {
                    throw new Exception($"Failed to complete saga data for SagaId='{sagaId}' and CorrelationId='{correlationId}'");
                }

                var dataAtReading  = context.Get <Metadata>().SagaData[sagaId];
                var dataAtDeletion = previouslyExistingState.Value;

                if (Compare(dataAtReading, dataAtDeletion) == false)
                {
                    throw new Exception($"Optimistic concurrency failure for SagaId='{sagaId}' and CorrelationId='{correlationId}'");
                }

                await tx.CommitAsync().ConfigureAwait(false);
            }
        }
Пример #9
0
        public async Task Update(IContainSagaData sagaData, SynchronizedStorageSession session, ContextBag context)
        {
            var sagas = await GetSagasDictionary().ConfigureAwait(false);

            var secondaryIndex = await GetIndexDicitonary().ConfigureAwait(false);

            using (var tx = stateManager.CreateTransaction())
            {
                var sagaId = sagaData.Id;

                var correlationId = await GetCorrelationId(secondaryIndex, tx, sagaId).ConfigureAwait(false);

                var data = Serialize(sagaData);

                var previousData = context.Get <Metadata>().SagaData[sagaData.Id];

                var result = await sagas.TryUpdateAsync(tx, correlationId, data, previousData);

                if (result == false)
                {
                    throw new Exception($"Optimistic concurrency failure for SagaId='{sagaData.Id}' and CorrelationId='{sagaId}'");
                }

                await tx.CommitAsync();
            }
        }
        public async Task <T> Get <T>(string propertyName, object propertyValue, SynchronizedStorageSession session, ContextBag context)
            where T : class, IContainSagaData
        {
            var documentSession = session.RavenSession();

            var lookupId = SagaUniqueIdentity.FormatId(typeof(T), propertyName, propertyValue);

            //store it in the context to be able to optimize deletes for legacy sagas that don't have the id in metadata
            context.Set(UniqueDocIdKey, lookupId);

            var lookup = await documentSession
                         .Include("SagaDocId") //tell raven to pull the saga doc as well to save us a round-trip
                         .LoadAsync <SagaUniqueIdentity>(lookupId)
                         .ConfigureAwait(false);

            if (lookup != null)
            {
                documentSession.Advanced.Evict(lookup);

                return(lookup.SagaDocId != null
                    ? await documentSession.LoadAsync <T>(lookup.SagaDocId).ConfigureAwait(false) //if we have a saga id we can just load it
                    : await Get <T>(lookup.SagaId, session, context).ConfigureAwait(false));      //if not this is a saga that was created pre 3.0.4 so we fallback to a get instead
            }

            return(default(T));
        }
 Task <T> ISagaPersister.Get <T>(string property, object value, SynchronizedStorageSession session, ContextBag context)
 {
     return(session.Session().CreateCriteria(typeof(T))
            .SetLockMode(GetLockModeForSaga <T>())
            .Add(Restrictions.Eq(property, value))
            .UniqueResultAsync <T>());
 }
        static Task SaveSagaWithoutCorrelationProperty(IContainSagaData sagaData, SynchronizedStorageSession session)
        {
            var indexStream      = BuildSagaByIdStreamName(sagaData.GetType(), sagaData.Id);
            var stateChangeEvent = new EventData(Guid.NewGuid(), SagaDataEventType, true, sagaData.ToJsonBytes(), new byte[0]);

            return(session.AppendToStreamAsync(indexStream, ExpectedVersion.NoStream, stateChangeEvent));
        }
Пример #13
0
        public async Task <T> Get <T>(string propertyName, object propertyValue, SynchronizedStorageSession session, ContextBag context)
            where T : class, IContainSagaData
        {
            var documentSession = session.RavenSession();

            var lookupId = SagaUniqueIdentity.FormatId(typeof(T), propertyName, propertyValue);

            var lookup = await documentSession
                         .Include("SagaDocId") //tell raven to pull the saga doc as well to save us a round-trip
                         .LoadAsync <SagaUniqueIdentity>(lookupId)
                         .ConfigureAwait(false);

            if (lookup != null)
            {
                documentSession.Advanced.Evict(lookup);

                // If we have a saga id we can just load it, should have been included in the round-trip already
                var container = await documentSession.LoadAsync <SagaDataContainer>(lookup.SagaDocId).ConfigureAwait(false);

                if (container != null)
                {
                    if (container.IdentityDocId == null)
                    {
                        container.IdentityDocId = lookupId;
                    }
                    context.Set($"{SagaContainerContextKeyPrefix}{container.Data.Id}", container);
                    return((T)container.Data);
                }
            }

            return(default(T));
        }
Пример #14
0
    public async Task <TSagaData> Get <TSagaData>(Guid sagaId, SynchronizedStorageSession session, ContextBag context)
        where TSagaData : class, IContainSagaData
    {
        var result = await Get <TSagaData>(sagaId, session).ConfigureAwait(false);

        return(SetConcurrency(result, context));
    }
Пример #15
0
        public async Task <ClientOutboxMessageV2> GetAsync(Guid messageId, SynchronizedStorageSession synchronizedStorageSession)
        {
            var sqlStorageSession = synchronizedStorageSession.GetSqlStorageSession();

            using (var command = sqlStorageSession.Connection.CreateCommand())
            {
                command.CommandText = GetCommandText;
                command.CommandType = CommandType.Text;
                command.Transaction = sqlStorageSession.Transaction;
                command.AddParameter("MessageId", messageId);

                using (var reader = await command.ExecuteReaderAsync(CommandBehavior.SingleRow).ConfigureAwait(false))
                {
                    if (await reader.ReadAsync().ConfigureAwait(false))
                    {
                        var endpointName        = reader.GetString(0);
                        var transportOperations = JsonConvert.DeserializeObject <List <TransportOperation> >(reader.GetString(1), new JsonSerializerSettings {
                            TypeNameHandling = TypeNameHandling.Auto
                        });
                        var clientOutboxMessage = new ClientOutboxMessageV2(messageId, endpointName, transportOperations);

                        return(clientOutboxMessage);
                    }

                    throw new KeyNotFoundException($"Client outbox data not found where MessageId = '{messageId}'");
                }
            }
        }
        public Task <T> Get <T>(Guid sagaId, SynchronizedStorageSession session, ContextBag context)
            where T : class, IContainSagaData
        {
            var result = session.Session().Get <T>(sagaId, GetLockModeForSaga <T>());

            return(Task.FromResult(result));
        }
        public async Task Save(
            IContainSagaData sagaData,
            SagaCorrelationProperty correlationProperty,
            SynchronizedStorageSession session,
            ContextBag context)
        {
            var sagaTypeName = sagaData.GetType().Name;

            var sagaDataWithVersion = sagaData as IHaveDocumentVersion;

            if (sagaDataWithVersion == null)
            {
                throw new InvalidOperationException(
                          string.Format("Saga type {0} does not implement IHaveDocumentVersion", sagaTypeName));
            }

            sagaDataWithVersion.DocumentVersion = 0;
            sagaDataWithVersion.ETag            = sagaData.ComputeETag();

            if (correlationProperty != null)
            {
                await this.EnsureUniqueIndex(sagaData, correlationProperty).ConfigureAwait(false);
            }

            var collection = this.mongoDatabase.GetCollection <BsonDocument>(sagaTypeName);
            await collection.InsertOneAsync(sagaData.ToBsonDocument()).ConfigureAwait(false);
        }
Пример #18
0
                public Task <TestSaga08.SagaData08> FindBy(SomeOtherMessage message, SynchronizedStorageSession storageSession, ReadOnlyContextBag context)
                {
                    Context.FinderUsed = true;
                    var bag = new ContextBag();

                    bag.Set(context.Get <IncomingMessage>());
                    return(SagaPersister.Get <TestSaga08.SagaData08>("Property", "Test", storageSession, bag));
                }
 public ReceiverDataContext GetDataContext(SynchronizedStorageSession storageSession)
 {
     if (context == null)
     {
         context = contextFactory(storageSession);
     }
     return(context);
 }
Пример #20
0
    public Task <OrderSagaData> FindBy(StartOrder message, SynchronizedStorageSession storageSession, ReadOnlyContextBag context)
    {
        var session = storageSession.RavenSession();

        // if the instance is null a new saga will be automatically created if
        // the Saga handles the message as IAmStartedByMessages<StartOrder>; otherwise an exception is raised.
        return(session.LoadByUniqueConstraintAsync <OrderSagaData>(d => d.OrderId, message.OrderId));
    }
 static StorageSession GetSqlStorageSession(this SynchronizedStorageSession session)
 {
     if (session is StorageSession storageSession)
     {
         return(storageSession);
     }
     throw new Exception("Cannot access the SQL synchronized storage session. Either this endpoint has not been configured to use the SQL persistence or a different persistence type is used for sagas.");
 }
Пример #22
0
        public Task <TweetReceivedSagaData> FindBy(TweetReceived message, SynchronizedStorageSession storageSession, ReadOnlyContextBag context)
        {
            var tweetReceivedSagaData = storageSession.Session().QueryOver <TweetReceivedSagaData>()
                                        .Where(d => d.Hashtag == message.Track)
                                        .SingleOrDefault();

            return(Task.FromResult(tweetReceivedSagaData));
        }
        public Task Complete(IContainSagaData sagaData, SynchronizedStorageSession session, ContextBag context)
        {
            var query = Builders <BsonDocument> .Filter.Eq(MongoPersistenceConstants.IdPropertyName, sagaData.Id);

            var collection = this.mongoDatabase.GetCollection <BsonDocument>(sagaData.GetType().Name);

            return(collection.DeleteOneAsync(query));
        }
Пример #24
0
    public Task <OrderSagaData> FindBy(CompleteOrder message, SynchronizedStorageSession storageSession, ReadOnlyContextBag context)
    {
        var orderSagaData = storageSession.Session().QueryOver <OrderSagaData>()
                            .Where(d => d.OrderId == message.OrderId)
                            .SingleOrDefault();

        return(Task.FromResult(orderSagaData));
    }
Пример #25
0
        public Task Complete(IContainSagaData sagaData, SynchronizedStorageSession session, ContextBag context)
        {
            var documentSession = session.MartenSession();

            documentSession.Delete <SagaDocument>(sagaData.Id);

            return(Task.CompletedTask);
        }
Пример #26
0
 public Task<MySagaData> FindBy(MyMessage message, SynchronizedStorageSession storageSession, ReadOnlyContextBag context)
 {
     // SynchronizedStorageSession will have a persistence specific extension method
     // For example purposes GetDbSession is a stub extension method
     var dbSession = storageSession.GetDbSession();
     return dbSession.GetSagaFromDB(message.SomeId, message.SomeData);
     // If a saga can't be found Task.FromResult(null) should be returned
 }
 static StorageSession GetSqlStorageSession(this SynchronizedStorageSession session)
 {
     if (session is StorageSession storageSession)
     {
         return(storageSession);
     }
     throw new Exception("The endpoint has not been configured to use SQL persistence.");
 }
Пример #28
0
        public static ISqlStorageSession GetSqlSession(this SynchronizedStorageSession synchronizedStorageSession)
        {
            if (synchronizedStorageSession is ISqlStorageSession sqlSession)
            {
                return(sqlSession);
            }

            throw new Exception("Cannot access the SQL session");
        }
Пример #29
0
 public InvokeHandlerContext(MessageHandler handler, string messageId, string replyToAddress, Dictionary<string, string> headers, MessageMetadata messageMetadata, object messageBeingHandled, SynchronizedStorageSession storageSession, IBehaviorContext parentContext)
     : base(messageId, replyToAddress, headers, parentContext)
 {
     MessageHandler = handler;
     Headers = headers;
     MessageBeingHandled = messageBeingHandled;
     MessageMetadata = messageMetadata;
     Set(storageSession);
 }
Пример #30
0
                public Task <SagaFinderSagaData> FindBy(StartSagaMessage message, SynchronizedStorageSession session, ReadOnlyContextBag options, CancellationToken cancellationToken = default)
                {
                    if (Context.SagaId == Guid.Empty)
                    {
                        return(Task.FromResult(default(SagaFinderSagaData)));
                    }

                    return(session.RavenSession().LoadAsync <SagaFinderSagaData>(Context.SagaId.ToString(), cancellationToken));
                }
 /// <summary>
 /// Gets the current context NHibernate <see cref="ISession"/>.
 /// </summary>
 public static ISession Session(this SynchronizedStorageSession session)
 {
     var ambientTransactionSession = session as INHibernateSynchronizedStorageSession;
     if (ambientTransactionSession != null)
     {
         return ambientTransactionSession.Session;
     }
     throw new InvalidOperationException("Shared session has not been configured for NHibernate.");
 }
Пример #32
0
 internal InvokeHandlerContext(MessageHandler handler, SynchronizedStorageSession storageSession, IIncomingLogicalMessageContext parentContext)
     : this(handler, parentContext.MessageId, parentContext.ReplyToAddress, parentContext.Headers, parentContext.Message.Metadata, parentContext.Message.Instance, storageSession, parentContext)
 {
 }
Пример #33
0
 public abstract Task<IContainSagaData> Find(IBuilder builder, SagaFinderDefinition finderDefinition, SynchronizedStorageSession storageSession, ContextBag context, object message);