Exemplo n.º 1
0
        public override async Task <IContainSagaData> Find(IServiceProvider builder, SagaFinderDefinition finderDefinition, SynchronizedStorageSession storageSession, ContextBag context, object message, IReadOnlyDictionary <string, string> messageHeaders, CancellationToken cancellationToken)
        {
            var headerName = (string)finderDefinition.Properties["message-header-name"];

            if (!messageHeaders.TryGetValue(headerName, out var messageHeaderValue))
            {
                var saga           = context.Get <ActiveSagaInstance>();
                var sagaEntityName = saga.Metadata.Name;
                var messageName    = finderDefinition.MessageTypeName;

                throw new Exception($"Message {messageName} mapped to saga {sagaEntityName} is missing a header used for correlation: {headerName}.");
            }

            var correlationPropertyName = (string)finderDefinition.Properties["saga-property-name"];
            var correlationPropertyType = (Type)finderDefinition.Properties["saga-property-type"];

            object convertedHeaderValue;

            try
            {
                convertedHeaderValue = TypeDescriptor.GetConverter(correlationPropertyType).ConvertFromInvariantString(messageHeaderValue);
            }
            catch (Exception exception)
            {
                var saga           = context.Get <ActiveSagaInstance>();
                var sagaEntityName = saga.Metadata.Name;
                var messageName    = finderDefinition.MessageTypeName;

                throw new Exception($"Message {messageName} mapped to saga {sagaEntityName} contains correlation header {headerName} value that cannot be cast to correlation property type {correlationPropertyType}: {messageHeaderValue}", exception);
            }

            var lookupValues = context.GetOrCreate <SagaLookupValues>();

            lookupValues.Add <TSagaData>(correlationPropertyName, convertedHeaderValue);

            if (convertedHeaderValue == null)
            {
                var saga           = context.Get <ActiveSagaInstance>();
                var sagaEntityName = saga.Metadata.Name;
                var messageName    = finderDefinition.MessageTypeName;

                throw new Exception($"Message {messageName} mapped to saga {sagaEntityName} has attempted to assign null to the correlation property {correlationPropertyName}. Correlation properties cannot be assigned null.");
            }

            if (correlationPropertyName.ToLower() == "id")
            {
                return(await persister.Get <TSagaData>((Guid)convertedHeaderValue, storageSession, context, cancellationToken).ConfigureAwait(false));
            }

            return(await persister.Get <TSagaData>(correlationPropertyName, convertedHeaderValue, storageSession, context, cancellationToken).ConfigureAwait(false));
        }
        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.");
        }
Exemplo n.º 3
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);
            }
        }
Exemplo n.º 4
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 Task <OutboxTransaction> BeginTransaction(ContextBag context)
        {
            var connection = GetConnection(context);
            var messageId  = context.Get <IncomingMessage>().MessageId;

            return(Task.FromResult <OutboxTransaction>(new EventStoreOutboxTransaction(GetStreamName(messageId), messageId, connection)));
        }
Exemplo n.º 6
0
        public override async Task <IContainSagaData> Find(IServiceProvider builder, SagaFinderDefinition finderDefinition, SynchronizedStorageSession storageSession, ContextBag context, object message, IReadOnlyDictionary <string, string> messageHeaders, CancellationToken cancellationToken = default)
        {
            var propertyAccessor = (Func <object, object>)finderDefinition.Properties["property-accessor"];
            var propertyValue    = propertyAccessor(message);

            var sagaPropertyName = (string)finderDefinition.Properties["saga-property-name"];

            var lookupValues = context.GetOrCreate <SagaLookupValues>();

            lookupValues.Add <TSagaData>(sagaPropertyName, propertyValue);

            if (propertyValue == null)
            {
                var saga           = context.Get <ActiveSagaInstance>();
                var sagaEntityName = saga.Metadata.Name;
                var messageName    = finderDefinition.MessageTypeName;

                throw new Exception($"Message {messageName} mapped to saga {sagaEntityName} has attempted to assign null to the correlation property {sagaPropertyName}. Correlation properties cannot be assigned null.");
            }

            if (sagaPropertyName.ToLower() == "id")
            {
                return(await sagaPersister.Get <TSagaData>((Guid)propertyValue, storageSession, context, cancellationToken).ConfigureAwait(false));
            }

            return(await sagaPersister.Get <TSagaData>(sagaPropertyName, propertyValue, storageSession, context, cancellationToken).ConfigureAwait(false));
        }
Exemplo n.º 7
0
        public Task <CompletableSynchronizedStorageSession> OpenSession(ContextBag context)
        {
            var message = context.Get <IncomingMessage>();
            var session = sessionCreator.OpenSession(message.Headers);
            var synchronizedStorageSession = new RavenDBSynchronizedStorageSession(session, true);

            return(Task.FromResult((CompletableSynchronizedStorageSession)synchronizedStorageSession));
        }
        public static Type GetSagaType(this ContextBag context)
        {
            var activeSagaInstance = context.Get <ActiveSagaInstance>();

            if (activeSagaInstance != null)
            {
                return(activeSagaInstance.Instance.GetType());
            }
            throw new Exception($"Expected to find an instance of {nameof(ActiveSagaInstance)} in the {nameof(ContextBag)}.");
        }
Exemplo n.º 9
0
        public Task <CompletableSynchronizedStorageSession> OpenSession(ContextBag context, CancellationToken cancellationToken = default)
        {
            var message = context.Get <IncomingMessage>();
            var session = sessionCreator.OpenSession(message.Headers);
            var synchronizedStorageSession = new RavenDBSynchronizedStorageSession(session, context, true);

            sessionHolder?.SetCurrentSession(session);

            return(Task.FromResult((CompletableSynchronizedStorageSession)synchronizedStorageSession));
        }
Exemplo n.º 10
0
        public Task Update(IContainSagaData sagaData, SynchronizedStorageSession session, ContextBag context)
        {
            // store the schema version in case it has changed
            var container       = context.Get <SagaDataContainer>($"{SagaContainerContextKeyPrefix}{sagaData.Id}");
            var documentSession = session.RavenSession();

            documentSession.StoreSchemaVersionInMetadata(container);

            // dirty tracking will do the rest for us
            return(Task.CompletedTask);
        }
Exemplo n.º 11
0
        public Task Complete(IContainSagaData sagaData, SynchronizedStorageSession session, ContextBag context)
        {
            var documentSession = session.RavenSession();
            var container       = context.Get <SagaDataContainer>($"{SagaContainerContextKeyPrefix}{sagaData.Id}");

            documentSession.Delete(container);
            if (container.IdentityDocId != null)
            {
                documentSession.Advanced.Defer(new DeleteCommandData(container.IdentityDocId, null));
            }
            return(Task.CompletedTask);
        }
        public async Task <TSagaData> Get <TSagaData>(string propertyName, object propertyValue, SynchronizedStorageSession session, ContextBag context) where TSagaData : IContainSagaData
        {
            var operations      = new SagaPersisterAtomicOperations(session);
            var incomingMessage = context.Get <IncomingMessage>();
            var messageId       = incomingMessage.MessageId;
            var streamName      = BuildSagaDataStreamName(typeof(TSagaData), propertyValue);
            var slice           = await session.ReadStreamEventsBackwardAsync(streamName, -1, 1, true).ConfigureAwait(false);

            if (slice.Status != SliceReadStatus.Success)
            {
                return(default(TSagaData));
            }
            return(await operations.ReadSagaData <TSagaData>(streamName, slice.Events[0], messageId));
        }
        IEventStoreConnection GetConnection(ContextBag context)
        {
            if (outboxConnection != null)
            {
                return(outboxConnection);
            }
            var transportTransaction = context.Get <TransportTransaction>();
            IEventStoreConnection connection;

            if (!transportTransaction.TryGet(out connection))
            {
                throw new Exception("EventStore persistence can only be used either with EventStore transport or requires explicitly.");
            }
            return(connection);
        }
Exemplo n.º 14
0
        public async Task SetAsDispatched(string messageId, ContextBag context, CancellationToken cancellationToken = default)
        {
            var setAsDispatchedHolder = context.Get <SetAsDispatchedHolder>();

            var partitionKey    = setAsDispatchedHolder.PartitionKey;
            var containerHolder = setAsDispatchedHolder.ContainerHolder;

            var operation = new OutboxDelete(new OutboxRecord
            {
                Id         = messageId,
                Dispatched = true
            }, partitionKey, serializer, ttlInSeconds, context);

            var transactionalBatch = containerHolder.Container.CreateTransactionalBatch(partitionKey);

            await transactionalBatch.ExecuteOperationAsync(operation, containerHolder.PartitionKeyPath, cancellationToken : cancellationToken).ConfigureAwait(false);
        }
Exemplo n.º 15
0
        public async Task <T> Get <T>(Guid sagaId, SynchronizedStorageSession session, ContextBag context, CancellationToken cancellationToken = default)
            where T : class, IContainSagaData
        {
            var documentSession = session.RavenSession();
            var docId           = DocumentIdForSagaData(documentSession, typeof(T), sagaId);

            if (enablePessimisticLocking)
            {
                var index = await AcquireLease(documentSession.Advanced.DocumentStore, docId, cancellationToken).ConfigureAwait(false);

                // only true if we always have synchronized storage session around which is a valid assumption
                context.Get <SagaDataLeaseHolder>().DocumentsIdsAndIndexes.Add((docId, index));
            }

            var container = await documentSession.LoadAsync <SagaDataContainer>(docId, cancellationToken).ConfigureAwait(false);

            if (container == null)
            {
                return(default);
 public Task <OutboxTransaction> BeginTransaction(ContextBag context)
 {
     return(Task.FromResult <OutboxTransaction>(new StupidTransaction(context.Get <IOutboxActorService>())));
 }