public async Task Save_fails_when_data_changes_between_read_and_update_on_same_thread()
        {
            var sagaId = Guid.NewGuid();
            var saga = new TestSagaData
            {
                Id = sagaId,
                SomeId = sagaId.ToString()
            };
            var persister = new InMemorySagaPersister();
            var insertSession = new InMemorySynchronizedStorageSession();
            await persister.Save(saga, SagaMetadataHelper.GetMetadata<TestSaga>(saga), insertSession, new ContextBag());
            await insertSession.CompleteAsync();

            var winningContext = new ContextBag();
            var record = await persister.Get<TestSagaData>(saga.Id, new InMemorySynchronizedStorageSession(), winningContext);
            var losingContext = new ContextBag();
            var staleRecord = await persister.Get<TestSagaData>("SomeId", sagaId.ToString(), new InMemorySynchronizedStorageSession(), losingContext);

            var winningSaveSession = new InMemorySynchronizedStorageSession();
            var losingSaveSession = new InMemorySynchronizedStorageSession();

            await persister.Update(record, winningSaveSession, winningContext);
            await persister.Update(staleRecord, losingSaveSession, losingContext);

            await winningSaveSession.CompleteAsync();

            Assert.That(async () => await losingSaveSession.CompleteAsync(), Throws.InstanceOf<Exception>().And.Message.StartsWith($"InMemorySagaPersister concurrency violation: saga entity Id[{saga.Id}] already saved."));
        }
Exemple #2
0
        public async Task Should_rollback_saga_completion()
        {
            var sagaData = new TestSagaData
            {
                SomeId = Guid.NewGuid().ToString(),
                SomethingWeCareAbout = "NServiceBus"
            };

            await SaveSaga(sagaData);

            var contextBag = configuration.GetContextBagForSagaStorage();

            using (var session = await configuration.SynchronizedStorage.OpenSession(contextBag))
            {
                var sagaFromStorage = await configuration.SagaStorage.Get <TestSagaData>(sagaData.Id, session, contextBag);

                await configuration.SagaStorage.Complete(sagaFromStorage, session, contextBag);

                // Do not complete
            }

            var nonCompletedSaga = await GetById <TestSagaData>(sagaData.Id);

            Assert.NotNull(nonCompletedSaga);
            Assert.That(nonCompletedSaga.SomethingWeCareAbout, Is.EqualTo("NServiceBus"));
        }
        public async Task Save_should_fail_when_data_changes_between_read_and_update_on_same_thread()
        {
            configuration.RequiresOptimisticConcurrencySupport();

            var correlationPropertyData = Guid.NewGuid().ToString();
            var sagaData = new TestSagaData {
                SomeId = correlationPropertyData, DateTimeProperty = DateTime.UtcNow
            };

            await SaveSaga(sagaData);

            var generatedSagaId = sagaData.Id;

            var startSecondTaskSync      = new TaskCompletionSource <bool>();
            var firstTaskCanCompleteSync = new TaskCompletionSource <bool>();
            var persister = configuration.SagaStorage;

            var firstTask = Task.Run(async() =>
            {
                var winningContext = configuration.GetContextBagForSagaStorage();
                using (var winningSaveSession = await configuration.SynchronizedStorage.OpenSession(winningContext, default))
                {
                    var record = await persister.Get <TestSagaData>(generatedSagaId, winningSaveSession, winningContext, default);

                    startSecondTaskSync.SetResult(true);
                    await firstTaskCanCompleteSync.Task;

                    record.DateTimeProperty = DateTime.UtcNow;
                    await persister.Update(record, winningSaveSession, winningContext, default);
                    await winningSaveSession.CompleteAsync(default);
Exemple #4
0
        public async Task Save_should_fail_when_data_changes_between_read_and_update()
        {
            configuration.RequiresOptimisticConcurrencySupport();

            var correlationPropertyData = Guid.NewGuid().ToString();
            var sagaData = new TestSagaData {
                SomeId = correlationPropertyData, DateTimeProperty = DateTime.UtcNow
            };

            await SaveSaga(sagaData);

            var generatedSagaId = sagaData.Id;

            ContextBag losingContext;
            CompletableSynchronizedStorageSession losingSaveSession;
            TestSagaData staleRecord;
            var          persister = configuration.SagaStorage;

            var winningContext = configuration.GetContextBagForSagaStorage();

            using (var winningSaveSession = await configuration.SynchronizedStorage.OpenSession(winningContext, default))
            {
                var record = await persister.Get <TestSagaData>(generatedSagaId, winningSaveSession, winningContext, default);

                losingContext     = configuration.GetContextBagForSagaStorage();
                losingSaveSession = await configuration.SynchronizedStorage.OpenSession(losingContext, default);

                staleRecord = await persister.Get <TestSagaData>("SomeId", correlationPropertyData, losingSaveSession, losingContext, default);

                record.DateTimeProperty = DateTime.UtcNow;
                await persister.Update(record, winningSaveSession, winningContext, default);

                await winningSaveSession.CompleteAsync(default);
        public async Task Should_rollback_updates()
        {
            var sagaData = new TestSagaData
            {
                SomeId = Guid.NewGuid().ToString(),
                SomethingWeCareAbout = "NServiceBus"
            };

            await SaveSaga(sagaData);

            var contextBag = configuration.GetContextBagForSagaStorage();

            using (var session = await configuration.SynchronizedStorage.OpenSession(contextBag, default))
            {
                var sagaFromStorage = await configuration.SagaStorage.Get <TestSagaData>(sagaData.Id, session, contextBag, default);

                sagaFromStorage.SomethingWeCareAbout = "Particular.Platform";

                await configuration.SagaStorage.Update(sagaFromStorage, session, contextBag, default);

                // Do not complete
            }

            var hopefullyNotUpdatedSaga = await GetById <TestSagaData>(sagaData.Id);

            Assert.NotNull(hopefullyNotUpdatedSaga);
            Assert.That(hopefullyNotUpdatedSaga.SomethingWeCareAbout, Is.EqualTo("NServiceBus"));
        }
        public async Task Should_complete()
        {
            configuration.RequiresPessimisticConcurrencySupport();

            var correlationPropertyData = Guid.NewGuid().ToString();
            var saga = new TestSagaData {
                SomeId = correlationPropertyData, DateTimeProperty = DateTime.UtcNow
            };

            await SaveSaga(saga);

            var firstSessionDateTimeValue  = DateTime.UtcNow.AddDays(-2);
            var secondSessionDateTimeValue = DateTime.UtcNow.AddDays(-1);

            var firstSessionGetDone  = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);
            var secondSessionGetDone = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);
            var persister            = configuration.SagaStorage;

            async Task FirstSession()
            {
                var firstContent = configuration.GetContextBagForSagaStorage();

                using (var firstSaveSession = await configuration.SynchronizedStorage.OpenSession(firstContent, default))
                {
                    var record = await persister.Get <TestSagaData>(saga.Id, firstSaveSession, firstContent, default);

                    firstSessionGetDone.SetResult(true);

                    record.DateTimeProperty = firstSessionDateTimeValue;
                    await persister.Update(record, firstSaveSession, firstContent, default);

                    await secondSessionGetDone.Task.ConfigureAwait(false);

                    await firstSaveSession.CompleteAsync(default);
Exemple #7
0
        public async Task Should_fail_with_timeout()
        {
            configuration.RequiresPessimisticConcurrencySupport();

            var correlationPropertyData = Guid.NewGuid().ToString();
            var saga = new TestSagaData {
                SomeId = correlationPropertyData, SagaProperty = "initial value"
            };

            await SaveSaga(saga);

            var firstSessionGetDone  = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);
            var secondSessionGetDone = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);
            var persister            = configuration.SagaStorage;

            async Task FirstSession()
            {
                var firstSessionContext = configuration.GetContextBagForSagaStorage();

                using (var firstSaveSession = await configuration.SynchronizedStorage.OpenSession(firstSessionContext, default))
                {
                    var record = await persister.Get <TestSagaData>(saga.Id, firstSaveSession, firstSessionContext, default);

                    firstSessionGetDone.SetResult(true);

                    await Task.Delay(1000).ConfigureAwait(false);

                    await secondSessionGetDone.Task.ConfigureAwait(false);

                    record.SagaProperty = "session 1 value";
                    await persister.Update(record, firstSaveSession, firstSessionContext, default);

                    await firstSaveSession.CompleteAsync(default);
Exemple #8
0
        public async Task RevisionIsIncrementedOnEachUpdate()
        {
            var sagaId = Guid.NewGuid();

            var initialTransientInstance = new TestSagaData {
                Id = sagaId, Data = "yes, den kender jeg"
            };

            Assert.That(initialTransientInstance.Revision, Is.EqualTo(0));

            await _sagaStorage.Insert(initialTransientInstance, _noCorrelationProperties);

            var loadedSagaData0 = await _sagaStorage.Find(typeof(TestSagaData), "Id", sagaId);

            Assert.That(loadedSagaData0.Revision, Is.EqualTo(0));
            Assert.That(initialTransientInstance.Revision, Is.EqualTo(0));

            await _sagaStorage.Update(loadedSagaData0, _noCorrelationProperties);

            var loadedSagaData1 = await _sagaStorage.Find(typeof(TestSagaData), "Id", sagaId);

            Assert.That(loadedSagaData0.Revision, Is.EqualTo(1));
            Assert.That(loadedSagaData1.Revision, Is.EqualTo(1));

            await _sagaStorage.Update(loadedSagaData1, _noCorrelationProperties);

            var loadedSagaData2 = await _sagaStorage.Find(typeof(TestSagaData), "Id", sagaId);

            Assert.That(loadedSagaData1.Revision, Is.EqualTo(2));
            Assert.That(loadedSagaData2.Revision, Is.EqualTo(2));
        }
        public async Task Save_process_is_repeatable()
        {
            configuration.RequiresOptimisticConcurrencySupport();

            var correlationPropertyData = Guid.NewGuid().ToString();
            var saga = new TestSagaData {
                SomeId = correlationPropertyData, DateTimeProperty = DateTime.UtcNow
            };

            await SaveSaga(saga);

            ContextBag losingContext1;
            CompletableSynchronizedStorageSession losingSaveSession1;
            TestSagaData staleRecord1;
            var          persister = configuration.SagaStorage;

            var winningContext1     = configuration.GetContextBagForSagaStorage();
            var winningSaveSession1 = await configuration.SynchronizedStorage.OpenSession(winningContext1, default);

            try
            {
                var record1 = await persister.Get <TestSagaData>(saga.Id, winningSaveSession1, winningContext1, default);

                losingContext1     = configuration.GetContextBagForSagaStorage();
                losingSaveSession1 = await configuration.SynchronizedStorage.OpenSession(losingContext1, default);

                staleRecord1 = await persister.Get <TestSagaData>("SomeId", correlationPropertyData, losingSaveSession1, losingContext1, default);

                record1.DateTimeProperty = DateTime.UtcNow;
                await persister.Update(record1, winningSaveSession1, winningContext1, default);

                await winningSaveSession1.CompleteAsync(default);
        public void Update_DbChangedEntityWithoutARowVersion__ShouldSaveSaga()
        {
            // arrange
            TestSagaData newSagaData       = AddSaga();
            string       expectedSomeProp1 = Guid.NewGuid().ToString();
            string       expectedSomeProp2 = Guid.NewGuid().ToString();

            var persisterRetrievedSagaData = _persister.Get <TestSagaData>(newSagaData.Id);

            // simulates another worker updating the saga after "this" saga retrieves it's saga
            using (var dbc = new TestDbContext())
            {
                var fromDb = dbc.TestSagaDatas.Find(newSagaData.Id);
                fromDb.SomeProp1 = expectedSomeProp1;
                dbc.SaveChanges();
            }

            // act
            persisterRetrievedSagaData.SomeProp2 = expectedSomeProp2;
            _persister.Update(persisterRetrievedSagaData);

            // assert
            using (var dbc = new TestDbContext())
            {
                var fromDb = dbc.TestSagaDatas.Find(newSagaData.Id);

                fromDb.Id.Should().Be(newSagaData.Id);
                fromDb.OriginalMessageId.Should().Be(newSagaData.OriginalMessageId);
                fromDb.Originator.Should().Be(newSagaData.Originator);

                fromDb.SomeProp1.Should().Be(expectedSomeProp1);
                fromDb.SomeProp2.Should().Be(expectedSomeProp2);
            }
        }
Exemple #11
0
        public async Task RevisionIsIncrementedOnPassedInInstanceWhenDeleting()
        {
            var sagaId = Guid.NewGuid();

            var instance = new TestSagaData {
                Id = sagaId, Data = "yes, den kender jeg"
            };
            var initialRevision = instance.Revision;

            await _sagaStorage.Insert(instance, _noCorrelationProperties);

            var revisionAfterInsert = instance.Revision;

            await _sagaStorage.Update(instance, _noCorrelationProperties);

            var revisionAfterUpdate = instance.Revision;

            await _sagaStorage.Delete(instance);

            var revisionAfterDelete = instance.Revision;

            Assert.That(initialRevision, Is.EqualTo(0), "Expected initial revisio (before any saga persister actions) to be 0");
            Assert.That(revisionAfterInsert, Is.EqualTo(0), "Expected revision after first INSERT to be 0 because this is the first revision");
            Assert.That(revisionAfterUpdate, Is.EqualTo(1), "Expected revision after UPDATE to be 1 because is has now been saved as REV 1");
            Assert.That(revisionAfterDelete, Is.EqualTo(2), "Expceted revision after DELETE to be 2 because it's the best bet revision number to use even though it has most likely been deleted for good from the underlying storage");
        }
 public void Should_delete_the_saga()
 {
     var saga = new TestSagaData { Id = Guid.NewGuid() };
     
     persister.Save(saga);
     Assert.NotNull(persister.Get<TestSagaData>(saga.Id));
     persister.Complete(saga);
     Assert.Null(persister.Get<TestSagaData>(saga.Id));
 }
        public void Complete_DetachedEntity_Throws()
        {
            // arrange
            TestSagaData newSagaData = AddSaga();

            // act
            Action action = () => _persister.Complete(newSagaData);

            // assert
            action.ShouldThrow <DeletingDetachedEntityException>();
        }
        public void Persister_returns_different_instance_of_saga_data()
        {
            var saga = new TestSagaData { Id = Guid.NewGuid() };
            persister.Save(saga);

            var returnedSaga1 = persister.Get<TestSagaData>(saga.Id);
            var returnedSaga2 = persister.Get<TestSagaData>("Id", saga.Id);
            Assert.AreNotSame(returnedSaga2, returnedSaga1);
            Assert.AreNotSame(returnedSaga1, saga);
            Assert.AreNotSame(returnedSaga2, saga);
        }
        public void Save_fails_when_data_changes_between_read_and_update_on_same_thread()
        {
            var saga = new TestSagaData { Id = Guid.NewGuid() };
            persister.Save(saga);

            var record = persister.Get<TestSagaData>(saga.Id);
            var staleRecord = persister.Get<TestSagaData>("Id", saga.Id);

            persister.Save(record);
            var exception = Assert.Throws<Exception>(() => persister.Save(staleRecord));
            Assert.IsTrue(exception.Message.StartsWith(string.Format("InMemorySagaPersister concurrency violation: saga entity Id[{0}] already saved.", saga.Id)));
        }
        public void Save_fails_when_data_changes_between_read_and_update()
        {
            var saga = new TestSagaData { Id = Guid.NewGuid() };
            persister.Save(saga);

            var returnedSaga1 = Task<TestSagaData>.Factory.StartNew(() => persister.Get<TestSagaData>(saga.Id)).Result;
            var returnedSaga2 = persister.Get<TestSagaData>("Id", saga.Id);

            persister.Save(returnedSaga1);
            var exception = Assert.Throws<Exception>(() => persister.Save(returnedSaga2));
            Assert.IsTrue(exception.Message.StartsWith(string.Format("InMemorySagaPersister concurrency violation: saga entity Id[{0}] already saved.", saga.Id)));
        }
Exemple #17
0
    public async Task Reset_WithData_ClearsData()
    {
        var testSagaData = new TestSagaData {
            RequestId = "1"
        };
        await _inMemorySagaStorage.Insert(testSagaData, _correlationProperties);

        Assert.That(_inMemorySagaStorage.Instances, Is.Not.Empty);

        _inMemorySagaStorage.Reset();
        Assert.That(_inMemorySagaStorage.Instances, Is.Empty);
    }
        public async Task Should_fail()
        {
            configuration.RequiresOptimisticConcurrencySupport();

            var correlationPropertyData = Guid.NewGuid().ToString();
            var saga = new TestSagaData {
                SomeId = correlationPropertyData, DateTimeProperty = DateTime.UtcNow
            };

            await SaveSaga(saga);

            var persister = configuration.SagaStorage;

            ContextBag losingContext;
            CompletableSynchronizedStorageSession losingSaveSession;
            TestSagaData staleRecord;

            var winningContext     = configuration.GetContextBagForSagaStorage();
            var winningSaveSession = await configuration.SynchronizedStorage.OpenSession(winningContext);

            try
            {
                var record = await persister.Get <TestSagaData>(saga.Id, winningSaveSession, winningContext);

                losingContext     = configuration.GetContextBagForSagaStorage();
                losingSaveSession = await configuration.SynchronizedStorage.OpenSession(losingContext);

                staleRecord = await persister.Get <TestSagaData>("SomeId", correlationPropertyData, losingSaveSession, losingContext);

                record.DateTimeProperty = DateTime.UtcNow;
                await persister.Update(record, winningSaveSession, winningContext);

                await winningSaveSession.CompleteAsync();
            }
            finally
            {
                winningSaveSession.Dispose();
            }

            try
            {
                Assert.That(async() =>
                {
                    await persister.Complete(staleRecord, losingSaveSession, losingContext);
                    await losingSaveSession.CompleteAsync();
                }, Throws.InstanceOf <Exception>());
            }
            finally
            {
                losingSaveSession.Dispose();
            }
        }
        public async Task Get_returns_different_instance_of_saga_data()
        {
            var correlationPropertyData = Guid.NewGuid().ToString();
            var saga = new TestSagaData {
                SomeId = correlationPropertyData, DateTimeProperty = DateTime.UtcNow
            };

            await SaveSaga(saga);

            var returnedSaga1 = await GetById <TestSagaData>(saga.Id);

            var returnedSaga2 = await GetById <TestSagaData>(saga.Id);

            Assert.AreNotSame(returnedSaga2, returnedSaga1);
            Assert.AreNotSame(returnedSaga1, saga);
            Assert.AreNotSame(returnedSaga2, saga);
        }
        public async Task Should_delete_the_saga()
        {
            var saga = new TestSagaData {
                SomeId = Guid.NewGuid().ToString(), DateTimeProperty = DateTime.UtcNow
            };

            await SaveSaga(saga);

            var context = configuration.GetContextBagForSagaStorage();

            using (var completeSession = await configuration.SynchronizedStorage.OpenSession(context, default))
            {
                var sagaData = await configuration.SagaStorage.Get <TestSagaData>(saga.Id, completeSession, context, default);

                await configuration.SagaStorage.Complete(sagaData, completeSession, context, default);

                await completeSession.CompleteAsync(default);
        public async Task Persister_returns_different_instance_of_saga_data()
        {
            var saga = new TestSagaData
            {
                Id = Guid.NewGuid()
            };
            var persister = new InMemorySagaPersister();
            var insertSession = new InMemorySynchronizedStorageSession();
            await persister.Save(saga, SagaMetadataHelper.GetMetadata<TestSaga>(saga), insertSession, new ContextBag());
            await insertSession.CompleteAsync();

            var returnedSaga1 = await persister.Get<TestSagaData>(saga.Id, new InMemorySynchronizedStorageSession(), new ContextBag());
            var returnedSaga2 = await persister.Get<TestSagaData>("Id", saga.Id, new InMemorySynchronizedStorageSession(), new ContextBag());
            Assert.AreNotSame(returnedSaga2, returnedSaga1);
            Assert.AreNotSame(returnedSaga1, saga);
            Assert.AreNotSame(returnedSaga2, saga);
        }
        private static TestSagaData AddSaga()
        {
            var sagaData = new TestSagaData
            {
                Id                = Guid.NewGuid(),
                Originator        = "originator yeah",
                OriginalMessageId = "original message id",
                SomeProp1         = "some prop 1",
                SomeProp2         = "somep prop 2"
            };

            using (var dbContext = new TestDbContext())
            {
                dbContext.TestSagaDatas.Add(sagaData);
                dbContext.SaveChanges();
            }
            return(sagaData);
        }
        public async Task Save_should_fail_when_data_changes_between_concurrent_instances()
        {
            configuration.RequiresDtcSupport();

            var persister = configuration.SagaStorage;
            var sagaData  = new TestSagaData {
                SomeId = Guid.NewGuid().ToString()
            };

            await SaveSaga(sagaData);

            var generatedSagaId = sagaData.Id;

            Assert.That(async() =>
            {
                var storageAdapter = configuration.SynchronizedStorageAdapter;
                using (var tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                {
                    Transaction.Current.EnlistDurable(EnlistmentWhichEnforcesDtcEscalation.Id, new EnlistmentWhichEnforcesDtcEscalation(), EnlistmentOptions.None);

                    var transportTransaction = new TransportTransaction();
                    transportTransaction.Set(Transaction.Current);

                    var unenlistedContextBag = configuration.GetContextBagForSagaStorage();
                    using (var unenlistedSession = await configuration.SynchronizedStorage.OpenSession(unenlistedContextBag))
                    {
                        var enlistedContextBag = configuration.GetContextBagForSagaStorage();
                        var enlistedSession    = await storageAdapter.TryAdapt(transportTransaction, enlistedContextBag);

                        var unenlistedRecord = await persister.Get <TestSagaData>(generatedSagaId, unenlistedSession, unenlistedContextBag);

                        var enlistedRecord = await persister.Get <TestSagaData>(generatedSagaId, enlistedSession, enlistedContextBag);

                        await persister.Update(unenlistedRecord, unenlistedSession, unenlistedContextBag);
                        await persister.Update(enlistedRecord, enlistedSession, enlistedContextBag);

                        await unenlistedSession.CompleteAsync();
                    }

                    tx.Complete();
                }
            }, Throws.Exception.TypeOf <TransactionAbortedException>());
        }
        public void Save_process_is_repeatable()
        {
            var saga = new TestSagaData { Id = Guid.NewGuid() };
            persister.Save(saga);

            var returnedSaga1 = Task<TestSagaData>.Factory.StartNew(() => persister.Get<TestSagaData>(saga.Id)).Result;
            var returnedSaga2 = persister.Get<TestSagaData>("Id", saga.Id);

            persister.Save(returnedSaga1);
            var exceptionFromSaga2 = Assert.Throws<Exception>(() => persister.Save(returnedSaga2));
            Assert.IsTrue(exceptionFromSaga2.Message.StartsWith(string.Format("InMemorySagaPersister concurrency violation: saga entity Id[{0}] already saved.", saga.Id)));

            var returnedSaga3 = Task<TestSagaData>.Factory.StartNew(() => persister.Get<TestSagaData>("Id", saga.Id)).Result;
            var returnedSaga4 = persister.Get<TestSagaData>(saga.Id);

            persister.Save(returnedSaga4);

            var exceptionFromSaga3 = Assert.Throws<Exception>(() => persister.Save(returnedSaga3));
            Assert.IsTrue(exceptionFromSaga3.Message.StartsWith(string.Format("InMemorySagaPersister concurrency violation: saga entity Id[{0}] already saved.", saga.Id)));
        }
        public async Task Save_fails_when_data_changes_between_concurrent_instances()
        {
            var saga = new TestSagaData
            {
                Id = Guid.NewGuid()
            };

            var persister = new InMemorySagaPersister();
            var storageAdapter = new InMemoryTransactionalSynchronizedStorageAdapter();
            var insertSession = new InMemorySynchronizedStorageSession();

            await persister.Save(saga, SagaMetadataHelper.GetMetadata<TestSaga>(saga), insertSession, new ContextBag());
            await insertSession.CompleteAsync();

            Assert.That(async () =>
            {
                using (var tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                {
                    Transaction.Current.EnlistDurable(EnlistmentWhichEnforcesDtcEscalation.Id, new EnlistmentWhichEnforcesDtcEscalation(), EnlistmentOptions.None);

                    var transportTransaction = new TransportTransaction();
                    transportTransaction.Set(Transaction.Current);

                    var unenlistedSession = new InMemorySynchronizedStorageSession();

                    var enlistedSession = await storageAdapter.TryAdapt(transportTransaction, new ContextBag());

                    var unenlistedSessionContext = new ContextBag();
                    var unenlistedRecord = await persister.Get<TestSagaData>(saga.Id, unenlistedSession, unenlistedSessionContext);
                    var enlistedSessionContext = new ContextBag();
                    var enlistedRecord = await persister.Get<TestSagaData>("Id", saga.Id, enlistedSession, enlistedSessionContext);

                    await persister.Update(unenlistedRecord, unenlistedSession, unenlistedSessionContext);
                    await persister.Update(enlistedRecord, enlistedSession, enlistedSessionContext);

                    await unenlistedSession.CompleteAsync();

                    tx.Complete();
                }
            }, Throws.Exception.TypeOf<TransactionAbortedException>());
        }
        public async Task Should_delete_the_saga()
        {
            var saga = new TestSagaData
            {
                Id = Guid.NewGuid()
            };
            var persister = new InMemorySagaPersister();
            var insertSession = new InMemorySynchronizedStorageSession();
            await persister.Save(saga, SagaMetadataHelper.GetMetadata<TestSaga>(saga), insertSession, new ContextBag());
            await insertSession.CompleteAsync();

            var intentionallySharedContext = new ContextBag();
            var sagaData = await persister.Get<TestSagaData>(saga.Id, new InMemorySynchronizedStorageSession(), intentionallySharedContext );

            var deleteSession = new InMemorySynchronizedStorageSession();
            await persister.Complete(saga, deleteSession, intentionallySharedContext );
            await deleteSession.CompleteAsync();
            var completedSaga = await persister.Get<TestSagaData>(saga.Id, new InMemorySynchronizedStorageSession(), new ContextBag());

            Assert.NotNull(sagaData);
            Assert.Null(completedSaga);
        }
        public async Task Should_rollback_storing_new_saga()
        {
            var sagaData = new TestSagaData
            {
                SomeId = Guid.NewGuid().ToString(),
                SomethingWeCareAbout = "NServiceBus"
            };

            var contextBag = configuration.GetContextBagForSagaStorage();

            using (var session = await configuration.SynchronizedStorage.OpenSession(contextBag, default))
            {
                await SaveSagaWithSession(sagaData, session, contextBag);

                // Do not complete
            }

            var sagaById = await GetById <TestSagaData>(sagaData.Id);

            Assert.IsNull(sagaById);
            var sagaByCorrelationProperty = await GetByCorrelationProperty <TestSagaData>(nameof(sagaData.SomeId), sagaData.SomeId);

            Assert.IsNull(sagaByCorrelationProperty);
        }
        public void Complete_UpdatedSagaWithoutARowVersion_DeletesSaga()
        {
            // arrange
            TestSagaData sagaData = AddSaga();
            var          persisterRetrievedSagaData = _persister.Get <TestSagaData>(sagaData.Id);

            using (var dbc = new TestDbContext())
            {
                TestSagaData foundSaga = dbc.TestSagaDatas.Find(sagaData.Id);
                foundSaga.Should().NotBeNull();

                foundSaga.SomeProp1 = Guid.NewGuid().ToString(); // update prop
                dbc.SaveChanges();
            }

            // act
            _persister.Complete(persisterRetrievedSagaData);

            // assert
            using (var dbContext = new TestDbContext())
            {
                dbContext.TestSagaDatas.Find(sagaData.Id).Should().BeNull();
            }
        }
Exemple #29
0
        public async Task Save_process_is_repeatable()
        {
            configuration.RequiresOptimisticConcurrencySupport();

            var correlationPropertyData = Guid.NewGuid().ToString();
            var saga = new TestSagaData {
                SomeId = correlationPropertyData, DateTimeProperty = DateTime.UtcNow
            };

            await SaveSaga(saga);

            ContextBag losingContext1;
            CompletableSynchronizedStorageSession losingSaveSession1;
            TestSagaData staleRecord1;
            var          persister = configuration.SagaStorage;

            var winningContext1     = configuration.GetContextBagForSagaStorage();
            var winningSaveSession1 = await configuration.SynchronizedStorage.OpenSession(winningContext1);

            try
            {
                var record1 = await persister.Get <TestSagaData>(saga.Id, winningSaveSession1, winningContext1);

                losingContext1     = configuration.GetContextBagForSagaStorage();
                losingSaveSession1 = await configuration.SynchronizedStorage.OpenSession(losingContext1);

                staleRecord1 = await persister.Get <TestSagaData>("SomeId", correlationPropertyData, losingSaveSession1, losingContext1);

                record1.DateTimeProperty = DateTime.UtcNow;
                await persister.Update(record1, winningSaveSession1, winningContext1);

                await winningSaveSession1.CompleteAsync();
            }
            finally
            {
                winningSaveSession1.Dispose();
            }

            try
            {
                Assert.That(async() =>
                {
                    await persister.Update(staleRecord1, losingSaveSession1, losingContext1);
                    await losingSaveSession1.CompleteAsync();
                }, Throws.InstanceOf <Exception>());
            }
            finally
            {
                losingSaveSession1.Dispose();
            }

            ContextBag losingContext2;
            CompletableSynchronizedStorageSession losingSaveSession2;
            TestSagaData staleRecord2;

            var winningContext2     = configuration.GetContextBagForSagaStorage();
            var winningSaveSession2 = await configuration.SynchronizedStorage.OpenSession(winningContext2);

            try
            {
                var record2 = await persister.Get <TestSagaData>(saga.Id, winningSaveSession2, winningContext2);

                losingContext2     = configuration.GetContextBagForSagaStorage();
                losingSaveSession2 = await configuration.SynchronizedStorage.OpenSession(losingContext2);

                staleRecord2 = await persister.Get <TestSagaData>("SomeId", correlationPropertyData, losingSaveSession2, losingContext2);

                record2.DateTimeProperty = DateTime.UtcNow;
                await persister.Update(record2, winningSaveSession2, winningContext2);

                await winningSaveSession2.CompleteAsync();
            }
            finally
            {
                winningSaveSession2.Dispose();
            }

            try
            {
                Assert.That(async() =>
                {
                    await persister.Update(staleRecord2, losingSaveSession2, losingContext2);
                    await losingSaveSession2.CompleteAsync();
                }, Throws.InstanceOf <Exception>());
            }
            finally
            {
                losingSaveSession2.Dispose();
            }
        }
        public async Task Save_process_is_repeatable()
        {
            var sagaId = Guid.NewGuid();
            var saga = new TestSagaData
            {
                Id = sagaId,
                SomeId = sagaId.ToString()
            };
            var persister = new InMemorySagaPersister();
            var insertSession = new InMemorySynchronizedStorageSession();
            await persister.Save(saga, SagaMetadataHelper.GetMetadata<TestSaga>(saga), insertSession, new ContextBag());
            await insertSession.CompleteAsync();

            var winningSessionContext = new ContextBag();
            var returnedSaga1 = await Task.Run(() => persister.Get<TestSagaData>(saga.Id, new InMemorySynchronizedStorageSession(), winningSessionContext));

            var losingSessionContext = new ContextBag();
            var returnedSaga2 = await persister.Get<TestSagaData>("SomeId", sagaId.ToString(), new InMemorySynchronizedStorageSession(), losingSessionContext);

            var winningSaveSession = new InMemorySynchronizedStorageSession();
            var losingSaveSession = new InMemorySynchronizedStorageSession();

            await persister.Update(returnedSaga1, winningSaveSession, winningSessionContext);
            await persister.Update(returnedSaga2, losingSaveSession, losingSessionContext);

            await winningSaveSession.CompleteAsync();
            Assert.That(async () => await losingSaveSession.CompleteAsync(), Throws.InstanceOf<Exception>().And.Message.StartsWith($"InMemorySagaPersister concurrency violation: saga entity Id[{saga.Id}] already saved."));

            losingSessionContext = new ContextBag();
            var returnedSaga3 = await Task.Run(() => persister.Get<TestSagaData>("SomeId", sagaId.ToString(), new InMemorySynchronizedStorageSession(), losingSessionContext));

            winningSessionContext = new ContextBag();
            var returnedSaga4 = await persister.Get<TestSagaData>(saga.Id, new InMemorySynchronizedStorageSession(), winningSessionContext);

            winningSaveSession = new InMemorySynchronizedStorageSession();
            losingSaveSession = new InMemorySynchronizedStorageSession();

            await persister.Update(returnedSaga4, winningSaveSession, winningSessionContext);
            await persister.Update(returnedSaga3, losingSaveSession, losingSessionContext);

            await winningSaveSession.CompleteAsync();

            Assert.That(async () => await losingSaveSession.CompleteAsync(), Throws.InstanceOf<Exception>().And.Message.StartsWith($"InMemorySagaPersister concurrency violation: saga entity Id[{saga.Id}] already saved."));
        }
        public async Task Should_complete()
        {
            configuration.RequiresPessimisticConcurrencySupport();

            var correlationPropertyData = Guid.NewGuid().ToString();
            var saga = new TestSagaData {
                SomeId = correlationPropertyData, DateTimeProperty = DateTime.UtcNow
            };

            await SaveSaga(saga);

            var firstSessionDateTimeValue  = DateTime.UtcNow.AddDays(-2);
            var secondSessionDateTimeValue = DateTime.UtcNow.AddDays(-1);

            var firstSessionGetDone  = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);
            var secondSessionGetDone = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);
            var persister            = configuration.SagaStorage;

            async Task FirstSession()
            {
                var firstContent = configuration.GetContextBagForSagaStorage();

                using (var firstSaveSession = await configuration.SynchronizedStorage.OpenSession(firstContent))
                {
                    var record = await persister.Get <TestSagaData>(saga.Id, firstSaveSession, firstContent);

                    firstSessionGetDone.SetResult(true);

                    record.DateTimeProperty = firstSessionDateTimeValue;
                    await persister.Update(record, firstSaveSession, firstContent);

                    await secondSessionGetDone.Task.ConfigureAwait(false);

                    await firstSaveSession.CompleteAsync();
                }
            }

            async Task SecondSession()
            {
                var secondContext = configuration.GetContextBagForSagaStorage();

                using (var secondSession = await configuration.SynchronizedStorage.OpenSession(secondContext))
                {
                    await firstSessionGetDone.Task.ConfigureAwait(false);

                    var recordTask = Task.Run(() => persister.Get <TestSagaData>(saga.Id, secondSession, secondContext));
                    secondSessionGetDone.SetResult(true);

                    var record = await recordTask.ConfigureAwait(false);

                    record.DateTimeProperty = secondSessionDateTimeValue;
                    await persister.Update(record, secondSession, secondContext);

                    await secondSession.CompleteAsync();
                }
            }

            await Task.WhenAll(SecondSession(), FirstSession());

            var result = await GetByCorrelationProperty <TestSagaData>(nameof(TestSagaData.SomeId), correlationPropertyData);

            // MongoDB stores datetime with less precision
            Assert.That(result.DateTimeProperty, Is.EqualTo(secondSessionDateTimeValue).Within(TimeSpan.FromMilliseconds(1)));
        }
        public async Task Save_fails_when_writing_same_data_twice()
        {
            var saga = new TestSagaData
            {
                Id = Guid.NewGuid()
            };
            var persister = new InMemorySagaPersister();
            var insertSession = new InMemorySynchronizedStorageSession();
            await persister.Save(saga, SagaMetadataHelper.GetMetadata<TestSaga>(saga), insertSession, new ContextBag());
            await insertSession.CompleteAsync();

            var retrievingContext = new ContextBag();
            var returnedSaga1 = await persister.Get<TestSagaData>(saga.Id, new InMemorySynchronizedStorageSession(), retrievingContext);

            var winningSaveSession = new InMemorySynchronizedStorageSession();
            var losingSaveSession = new InMemorySynchronizedStorageSession();

            await persister.Update(returnedSaga1, winningSaveSession, retrievingContext);
            await persister.Update(returnedSaga1, losingSaveSession, retrievingContext);

            await winningSaveSession.CompleteAsync();

            Assert.That(async () => await losingSaveSession.CompleteAsync(), Throws.InstanceOf<Exception>().And.Message.StartsWith($"InMemorySagaPersister concurrency violation: saga entity Id[{saga.Id}] already saved."));
        }
        public async Task Should_fail_with_timeout()
        {
            configuration.RequiresPessimisticConcurrencySupport();

            var correlationPropertyData = Guid.NewGuid().ToString();
            var saga = new TestSagaData {
                SomeId = correlationPropertyData, SagaProperty = "initial value"
            };

            await SaveSaga(saga);

            var firstSessionGetDone  = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);
            var secondSessionGetDone = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);
            var persister            = configuration.SagaStorage;

            async Task FirstSession()
            {
                var firstSessionContext = configuration.GetContextBagForSagaStorage();

                using (var firstSaveSession = await configuration.SynchronizedStorage.OpenSession(firstSessionContext))
                {
                    var record = await persister.Get <TestSagaData>(saga.Id, firstSaveSession, firstSessionContext);

                    firstSessionGetDone.SetResult(true);

                    await Task.Delay(1000).ConfigureAwait(false);

                    await secondSessionGetDone.Task.ConfigureAwait(false);

                    record.SagaProperty = "session 1 value";
                    await persister.Update(record, firstSaveSession, firstSessionContext);

                    await firstSaveSession.CompleteAsync();
                }
            }

            async Task SecondSession()
            {
                var secondContext = configuration.GetContextBagForSagaStorage();

                using (var secondSession = await configuration.SynchronizedStorage.OpenSession(secondContext))
                {
                    await firstSessionGetDone.Task.ConfigureAwait(false);

                    var recordTask = persister.Get <TestSagaData>(saga.Id, secondSession, secondContext);
                    secondSessionGetDone.SetResult(true);

                    var record = await recordTask.ConfigureAwait(false);

                    record.SagaProperty = "session 2 value";
                    await persister.Update(record, secondSession, secondContext);

                    await secondSession.CompleteAsync();
                }
            }

            var firstSessionTask  = FirstSession();
            var secondSessionTask = SecondSession();

            Assert.DoesNotThrowAsync(async() => await firstSessionTask);
            Assert.CatchAsync <Exception>(async() => await secondSessionTask); // not all persisters guarantee a TimeoutException

            var updatedSaga = await GetById <TestSagaData>(saga.Id);

            Assert.That(updatedSaga.SagaProperty, Is.EqualTo("session 1 value"));
        }