Example #1
0
 static IServiceFabricStorageSession GetServiceFabricSession(this ISynchronizedStorageSession session)
 {
     if (session is IServiceFabricStorageSession storageSession)
     {
         return(storageSession);
     }
     throw new Exception("The endpoint has not been configured to use Service Fabric persistence.");
 }
        public Task Update(IContainSagaData sagaData, ISynchronizedStorageSession session, ContextBag context, CancellationToken cancellationToken = default)
        {
            var storageSession = (StorageSession)session;
            var partitionKey   = GetPartitionKey(context, sagaData.Id);

            storageSession.AddOperation(new SagaUpdate(sagaData, partitionKey, serializer, context));
            return(Task.CompletedTask);
        }
Example #3
0
        /// <summary>
        /// Retrieves the current MongoDB client session from the context.
        /// </summary>
        public static IClientSessionHandle GetClientSession(this ISynchronizedStorageSession session)
        {
            Guard.AgainstNull(nameof(session), session);

            if (session is IMongoSessionProvider storageSession)
            {
                return(storageSession.MongoSession);
            }

            throw new Exception($"Cannot access the synchronized storage session. Ensure that 'EndpointConfiguration.UsePersistence<{nameof(MongoPersistence)}>()' has been called.");
        }
Example #4
0
        /// <summary>
        /// Retrieves the shared <see cref="ICosmosStorageSession"/> from the <see cref="ISynchronizedStorageSession"/>.
        /// </summary>
        public static ICosmosStorageSession CosmosPersistenceSession(this ISynchronizedStorageSession session)
        {
            Guard.AgainstNull(nameof(session), session);

            if (session is IWorkWithSharedTransactionalBatch workWith)
            {
                return(workWith.Create());
            }

            throw new Exception($"Cannot access the synchronized storage session. Ensure that 'EndpointConfiguration.UsePersistence<{nameof(CosmosPersistence)}>()' has been called.");
        }
        public Task <TSagaData> Get <TSagaData>(Guid sagaId, ISynchronizedStorageSession session, ContextBag context, CancellationToken cancellationToken = default)
            where TSagaData : class, IContainSagaData
        {
            if (sagas.TryGetValue(sagaId, out var value))
            {
                SetEntry(context, sagaId, value);

                var data = value.GetSagaCopy();
                return(Task.FromResult((TSagaData)data));
            }

            return(CachedSagaDataTask <TSagaData> .Default);
        }
        public Task Update(IContainSagaData sagaData, ISynchronizedStorageSession session, ContextBag context, CancellationToken cancellationToken = default)
        {
            ((NonDurableSynchronizedStorageSession)session).Enlist(() =>
            {
                var entry = GetEntry(context, sagaData.Id);

                if (sagas.TryUpdate(sagaData.Id, entry.UpdateTo(sagaData), entry) == false)
                {
                    throw new Exception($"NonDurableSagaPersister concurrency violation: saga entity Id[{sagaData.Id}] already saved.");
                }
            });

            return(Task.CompletedTask);
        }
Example #7
0
        public async Task Complete(IContainSagaData sagaData, ISynchronizedStorageSession session, ContextBag context, CancellationToken cancellationToken = default)
        {
            var storageSession = (StorageSession)session;
            var sagaDataType   = sagaData.GetType();

            var version = storageSession.RetrieveVersion(sagaDataType);

            var result = await storageSession.DeleteOneAsync(sagaDataType, filterBuilder.Eq(idElementName, sagaData.Id)& filterBuilder.Eq(versionElementName, version), cancellationToken).ConfigureAwait(false);

            if (result.DeletedCount != 1)
            {
                throw new Exception("Saga can't be completed because it was updated by another process.");
            }
        }
Example #8
0
        public async Task Update(IContainSagaData sagaData, ISynchronizedStorageSession session, ContextBag context, CancellationToken cancellationToken = default)
        {
            var storageSession = (StorageSession)session;
            var sagaDataType   = sagaData.GetType();

            var version  = storageSession.RetrieveVersion(sagaDataType);
            var document = sagaData.ToBsonDocument().SetElement(new BsonElement(versionElementName, version + 1));

            var result = await storageSession.ReplaceOneAsync(sagaDataType, filterBuilder.Eq(idElementName, sagaData.Id)& filterBuilder.Eq(versionElementName, version), document, cancellationToken).ConfigureAwait(false);

            if (result.ModifiedCount != 1)
            {
                throw new Exception($"The '{sagaDataType.Name}' saga with id '{sagaData.Id}' was updated by another process or no longer exists.");
            }
        }
        public async Task Complete(IContainSagaData sagaData, ISynchronizedStorageSession session, ContextBag context, CancellationToken cancellationToken = default)
        {
            var storageSession = (StorageSession)session;

            var entry = GetEntry(context, sagaData.Id);

            var sagaInfo = sagaInfoCache.GetInfo(sagaData.GetType());
            var sagas    = await storageSession.Sagas(sagaInfo.SagaAttribute.CollectionName, cancellationToken : cancellationToken).ConfigureAwait(false);

            var conditionalValue = await sagas.TryRemoveAsync(storageSession.Transaction, sagaData.Id, storageSession.TransactionTimeout, cancellationToken).ConfigureAwait(false);

            if (conditionalValue.HasValue && conditionalValue.Value.Data != entry.Data)
            {
                throw new Exception("Saga can't be completed as it was updated by another process.");
            }
        }
        public async Task <TSagaData> Get <TSagaData>(Guid sagaId, ISynchronizedStorageSession session, ContextBag context, CancellationToken cancellationToken = default)
            where TSagaData : class, IContainSagaData
        {
            var storageSession = (StorageSession)session;

            var sagaInfo = sagaInfoCache.GetInfo(typeof(TSagaData));
            var sagas    = await storageSession.Sagas(sagaInfo.SagaAttribute.CollectionName, cancellationToken : cancellationToken).ConfigureAwait(false);

            var conditionalValue = await sagas.TryGetValueAsync(storageSession.Transaction, sagaId, LockMode.Update, storageSession.TransactionTimeout, cancellationToken).ConfigureAwait(false);

            if (conditionalValue.HasValue)
            {
                SetEntry(context, sagaId, conditionalValue.Value);

                return(sagaInfo.FromSagaEntry <TSagaData>(conditionalValue.Value));
            }

            return(default);
        public async Task <TSagaData> Get <TSagaData>(Guid sagaId, ISynchronizedStorageSession session, ContextBag context, CancellationToken cancellationToken = default) where TSagaData : class, IContainSagaData
        {
            var storageSession = (StorageSession)session;

            // reads need to go directly
            var container    = storageSession.ContainerHolder.Container;
            var partitionKey = GetPartitionKey(context, sagaId);

            bool      sagaNotFound;
            TSagaData sagaData;

            if (!pessimisticLockingEnabled)
            {
                (sagaNotFound, sagaData) = await ReadSagaData <TSagaData>(sagaId, context, container, partitionKey, cancellationToken).ConfigureAwait(false);

                // if the previous lookup by id wasn't successful and the migration mode is enabled try to query for the saga data because the saga id probably represents
                // the saga id of the migrated saga.
                if (sagaNotFound && migrationModeEnabled &&
                    await FindSagaIdInMigrationMode(sagaId, context, container, cancellationToken).ConfigureAwait(false) is { } migratedSagaId)
                {
                    partitionKey  = GetPartitionKey(context, migratedSagaId);
                    (_, sagaData) = await ReadSagaData <TSagaData>(migratedSagaId, context, container, partitionKey, cancellationToken).ConfigureAwait(false);
                }
                return(sagaData);
            }

            (sagaNotFound, sagaData) = await AcquireLease <TSagaData>(sagaId, context, container, partitionKey, cancellationToken).ConfigureAwait(false);

            // if the previous lookup by id wasn't successful and the migration mode is enabled try to query for the saga data because the saga id probably represents
            // the saga id of the migrated saga.
            if (sagaNotFound && migrationModeEnabled &&
                await FindSagaIdInMigrationMode(sagaId, context, container, cancellationToken).ConfigureAwait(false) is { } previousSagaId)
            {
                partitionKey  = GetPartitionKey(context, previousSagaId);
                (_, sagaData) = await AcquireLease <TSagaData>(previousSagaId, context, container, partitionKey, cancellationToken).ConfigureAwait(false);
            }

            storageSession.AddOperation(new SagaReleaseLock(sagaData, partitionKey, serializer, context));

            return(sagaData);
        }
        public Task Complete(IContainSagaData sagaData, ISynchronizedStorageSession session, ContextBag context, CancellationToken cancellationToken = default)
        {
            ((NonDurableSynchronizedStorageSession)session).Enlist(() =>
            {
                var entry = GetEntry(context, sagaData.Id);

                if (sagasCollection.Remove(new KeyValuePair <Guid, Entry>(sagaData.Id, entry)) == false)
                {
                    throw new Exception("Saga can't be completed as it was updated by another process.");
                }

                // saga removed
                // clean the index
                if (Equals(entry.CorrelationId, NoCorrelationId) == false)
                {
                    byCorrelationIdCollection.Remove(new KeyValuePair <CorrelationId, Guid>(entry.CorrelationId, sagaData.Id));
                }
            });

            return(Task.CompletedTask);
        }
Example #13
0
        async Task <TSagaData> GetSagaData <TSagaData>(string elementName, object elementValue, ISynchronizedStorageSession session, CancellationToken cancellationToken)
        {
            var storageSession = (StorageSession)session;

            var document = await storageSession.Find <TSagaData>(new BsonDocument(elementName, BsonValue.Create(elementValue)), cancellationToken).ConfigureAwait(false);

            if (document != null)
            {
                var version = document.GetValue(versionElementName);
                storageSession.StoreVersion <TSagaData>(version.AsInt32);

                return(BsonSerializer.Deserialize <TSagaData>(document));
            }

            return(default);
Example #14
0
        public async Task Save(IContainSagaData sagaData, SagaCorrelationProperty correlationProperty, ISynchronizedStorageSession session, ContextBag context, CancellationToken cancellationToken = default)
        {
            var storageSession = (StorageSession)session;
            var sagaDataType   = sagaData.GetType();

            var document = sagaData.ToBsonDocument();

            document.Add(versionElementName, 0);

            await storageSession.InsertOneAsync(sagaDataType, document, cancellationToken).ConfigureAwait(false);
        }
        public Task Save(IContainSagaData sagaData, SagaCorrelationProperty correlationProperty, ISynchronizedStorageSession session, ContextBag context, CancellationToken cancellationToken = default)
        {
            ((NonDurableSynchronizedStorageSession)session).Enlist(() =>
            {
                var correlationId = NoCorrelationId;
                if (correlationProperty != SagaCorrelationProperty.None)
                {
                    correlationId = new CorrelationId(sagaData.GetType(), correlationProperty);
                    if (byCorrelationId.TryAdd(correlationId, sagaData.Id) == false)
                    {
                        throw new InvalidOperationException($"The saga with the correlation id 'Name: {correlationProperty.Name} Value: {correlationProperty.Value}' already exists");
                    }
                }

                var entry = new Entry(sagaData, correlationId);
                if (sagas.TryAdd(sagaData.Id, entry) == false)
                {
                    throw new Exception("A saga with this identifier already exists. This should never happened as saga identifier are meant to be unique.");
                }
            });

            return(Task.CompletedTask);
        }
        public Task <TSagaData> Get <TSagaData>(string propertyName, object propertyValue, ISynchronizedStorageSession session, ContextBag context, CancellationToken cancellationToken = default)
            where TSagaData : class, IContainSagaData
        {
            var key = new CorrelationId(typeof(TSagaData), propertyName, propertyValue);

            if (byCorrelationId.TryGetValue(key, out var id))
            {
                // this isn't updated atomically and may return null for an entry that has been indexed but not inserted yet
                return(Get <TSagaData>(id, session, context, cancellationToken));
            }

            return(CachedSagaDataTask <TSagaData> .Default);
        }
Example #17
0
 public Task <TSagaData> Get <TSagaData>(Guid sagaId, ISynchronizedStorageSession session, ContextBag context, CancellationToken cancellationToken = default) where TSagaData : class, IContainSagaData =>
 GetSagaData <TSagaData>(idElementName, sagaId, session, cancellationToken);
        public async Task <TSagaData> Get <TSagaData>(string propertyName, object propertyValue, ISynchronizedStorageSession session, ContextBag context, CancellationToken cancellationToken = default)
            where TSagaData : class, IContainSagaData
        {
            var storageSession = (StorageSession)session;

            // Saga ID needs to be calculated the same way as in SagaIdGenerator does
            var sagaId = CosmosSagaIdGenerator.Generate(typeof(TSagaData), propertyName, propertyValue);

            // reads need to go directly
            var container    = storageSession.ContainerHolder.Container;
            var partitionKey = GetPartitionKey(context, sagaId);

            TSagaData sagaData;

            if (!pessimisticLockingEnabled)
            {
                (_, sagaData) = await ReadSagaData <TSagaData>(sagaId, context, container, partitionKey, cancellationToken).ConfigureAwait(false);

                return(sagaData);
            }

            (_, sagaData) = await AcquireLease <TSagaData>(sagaId, context, container, partitionKey, cancellationToken).ConfigureAwait(false);

            storageSession.AddOperation(new SagaReleaseLock(sagaData, partitionKey, serializer, context));
            return(sagaData);
        }
Example #19
0
 /// <summary>
 /// Gets the current context ServiceFabricPersistence <see cref="IServiceFabricStorageSession"/>.
 /// </summary>
 public static IServiceFabricStorageSession ServiceFabricSession(this ISynchronizedStorageSession session)
 {
     Guard.AgainstNull(nameof(session), session);
     return(GetServiceFabricSession(session));
 }
Example #20
0
 public Task <TSagaData> Get <TSagaData>(string propertyName, object propertyValue, ISynchronizedStorageSession session, ContextBag context, CancellationToken cancellationToken = default) where TSagaData : class, IContainSagaData =>
 GetSagaData <TSagaData>(typeof(TSagaData).GetElementName(propertyName), propertyValue, session, cancellationToken);