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.")); }
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);
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);
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);
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); } }
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))); }
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(); } }
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")); }