public async Task Should_execute_operations_with_same_partition_key_together() { var fakeContainer = new FakeContainer(); var fakeCosmosClient = new FakeCosmosClient(fakeContainer); var containerHolderHolderResolver = new ContainerHolderResolver(new FakeProvider(fakeCosmosClient), new ContainerInformation("fakeContainer", new PartitionKeyPath("/deep/down")), "fakeDatabase"); var storageSession = new StorageSession(containerHolderHolderResolver, new ContextBag(), true); var firstOperation = new FakeOperation { PartitionKey = new PartitionKey("PartitionKey1") }; storageSession.AddOperation(firstOperation); var secondOperation = new FakeOperation { PartitionKey = new PartitionKey("PartitionKey1") }; storageSession.AddOperation(secondOperation); await((ICompletableSynchronizedStorageSession)storageSession).CompleteAsync(); Assert.That(firstOperation.WasApplied, Is.True); Assert.That(secondOperation.WasApplied, Is.True); Assert.That(firstOperation.AppliedBatch, Is.EqualTo(secondOperation.AppliedBatch), "Operations with the same partition key must be in the same batch"); }
public async Task Should_not_dispose_release_operations_when_operations_not_successful() { var fakeContainer = new FakeContainer(); var fakeCosmosClient = new FakeCosmosClient(fakeContainer); var containerHolderHolderResolver = new ContainerHolderResolver(new FakeProvider(fakeCosmosClient), new ContainerInformation("fakeContainer", new PartitionKeyPath("/deep/down")), "fakeDatabase"); var storageSession = new StorageSession(containerHolderHolderResolver, new ContextBag(), true); var operation = new ThrowOnApplyOperation { PartitionKey = new PartitionKey("PartitionKey1") }; storageSession.AddOperation(operation); var releaseOperation = new ReleaseLockOperation { PartitionKey = new PartitionKey("PartitionKey1") }; storageSession.AddOperation(releaseOperation); try { await((ICompletableSynchronizedStorageSession)storageSession).CompleteAsync(); } catch { // ignored } Assert.That(releaseOperation.WasApplied, Is.False); Assert.That(releaseOperation.WasDisposed, Is.False); }
public void Should_execute_and_dispose_release_operations_with_different_partition_key_distinct_when_not_completed() { var fakeContainer = new FakeContainer(); var fakeCosmosClient = new FakeCosmosClient(fakeContainer); var containerHolderHolderResolver = new ContainerHolderResolver(new FakeProvider(fakeCosmosClient), new ContainerInformation("fakeContainer", new PartitionKeyPath("/deep/down")), "fakeDatabase"); var storageSession = new StorageSession(containerHolderHolderResolver, new ContextBag(), true); var firstReleaseOperation = new ReleaseLockOperation { PartitionKey = new PartitionKey("PartitionKey1") }; storageSession.AddOperation(firstReleaseOperation); var secondReleaseOperation = new ReleaseLockOperation { PartitionKey = new PartitionKey("PartitionKey2") }; storageSession.AddOperation(secondReleaseOperation); storageSession.Dispose(); Assert.That(firstReleaseOperation.WasApplied, Is.True); Assert.That(secondReleaseOperation.WasApplied, Is.True); Assert.That(firstReleaseOperation.WasDisposed, Is.True); Assert.That(secondReleaseOperation.WasDisposed, Is.True); Assert.That(firstReleaseOperation.AppliedBatch, Is.Not.EqualTo(secondReleaseOperation.AppliedBatch), "Release operations with the different partition keys must be in different batches."); }
public void Should_execute_and_dispose_release_operations_as_best_effort() { var fakeContainer = new FakeContainer { TransactionalBatchFactory = () => new ThrowsOnExecuteAsyncTransactionalBatch() }; var fakeCosmosClient = new FakeCosmosClient(fakeContainer); var containerHolderHolderResolver = new ContainerHolderResolver(new FakeProvider(fakeCosmosClient), new ContainerInformation("fakeContainer", new PartitionKeyPath("/deep/down")), "fakeDatabase"); var storageSession = new StorageSession(containerHolderHolderResolver, new ContextBag(), true); var firstReleaseOperation = new ReleaseLockOperation { PartitionKey = new PartitionKey("PartitionKey1") }; storageSession.AddOperation(firstReleaseOperation); var secondReleaseOperation = new ReleaseLockOperation { PartitionKey = new PartitionKey("PartitionKey2") }; storageSession.AddOperation(secondReleaseOperation); Assert.DoesNotThrow(() => storageSession.Dispose()); Assert.That(firstReleaseOperation.WasApplied, Is.True); Assert.That(secondReleaseOperation.WasApplied, Is.True); Assert.That(firstReleaseOperation.WasDisposed, Is.True); Assert.That(secondReleaseOperation.WasDisposed, Is.True); }
public void Should_throw_when_no_container_available() { var fakeCosmosClient = new FakeCosmosClient(null); var containerHolderHolderResolver = new ContainerHolderResolver(new FakeProvider(fakeCosmosClient), null, "fakeDatabase"); var storageSession = new StorageSession(containerHolderHolderResolver, new ContextBag(), true); var operation = new FakeOperation(); storageSession.AddOperation(operation); var exception = Assert.ThrowsAsync <Exception>(async() => await((ICompletableSynchronizedStorageSession)storageSession).CompleteAsync()); Assert.That(exception.Message, Is.EqualTo("Unable to retrieve the container name and the partition key during processing. Make sure that either `persistence.Container()` is used or the relevant container information is available on the message handling pipeline.")); }
public async Task Should_clear_added_pending_operations_and_restore_ones_from_outbox_record() { var messageId = Guid.NewGuid().ToString(); var transportOperations = new[] { new TransportOperation( messageId: "42", properties: new DispatchProperties { { "Destination", "somewhere" } }, body: Array.Empty <byte>(), headers: new Dictionary <string, string>()), }; var fakeCosmosClient = new FakeCosmosClient(new FakeContainer { ReadItemStreamOutboxRecord = (id, key) => new OutboxRecord { Dispatched = false, Id = messageId, TransportOperations = transportOperations.Select(op => new StorageTransportOperation(op)) .ToArray() } }); var containerHolderHolderResolver = new ContainerHolderResolver(new FakeProvider(fakeCosmosClient), new ContainerInformation("fakeContainer", new PartitionKeyPath("")), "fakeDatabase"); var behavior = new OutboxBehavior(containerHolderHolderResolver, new JsonSerializer()); var testableContext = new TestableIncomingLogicalMessageContext(); testableContext.Extensions.Set(new PartitionKey("")); testableContext.Extensions.Set(new SetAsDispatchedHolder()); testableContext.Extensions.Set <IOutboxTransaction>(new CosmosOutboxTransaction(containerHolderHolderResolver, testableContext.Extensions)); var pendingTransportOperations = new PendingTransportOperations(); pendingTransportOperations.Add(new Transport.TransportOperation(new OutgoingMessage(null, null, null), null)); testableContext.Extensions.Set(pendingTransportOperations); await behavior.Invoke(testableContext, c => Task.CompletedTask); Assert.IsTrue(pendingTransportOperations.HasOperations, "Should have exactly one operation added found on the outbox record"); Assert.AreEqual("42", pendingTransportOperations.Operations.ElementAt(0).Message.MessageId, "Should have exactly one operation added found on the outbox record"); }
public void Should_dispose_operations() { var fakeCosmosClient = new FakeCosmosClient(null); var containerHolderHolderResolver = new ContainerHolderResolver(new FakeProvider(fakeCosmosClient), null, "fakeDatabase"); var storageSession = new StorageSession(containerHolderHolderResolver, new ContextBag(), true); var firstOperation = new FakeOperation(); storageSession.AddOperation(firstOperation); var secondOperation = new FakeOperation(); storageSession.AddOperation(secondOperation); storageSession.Dispose(); Assert.That(firstOperation.WasDisposed, Is.True); Assert.That(firstOperation.WasApplied, Is.False); Assert.That(secondOperation.WasDisposed, Is.True); Assert.That(secondOperation.WasApplied, Is.False); }
public async Task Should_not_complete_when_marked_as_do_not_complete() { var fakeCosmosClient = new FakeCosmosClient(null); var containerHolderHolderResolver = new ContainerHolderResolver(new FakeProvider(fakeCosmosClient), new ContainerInformation("fakeContainer", new PartitionKeyPath("")), "fakeDatabase"); var storageSession = new StorageSession(containerHolderHolderResolver, new ContextBag(), false); var firstOperation = new FakeOperation(); storageSession.AddOperation(firstOperation); var secondOperation = new FakeOperation(); storageSession.AddOperation(secondOperation); await((ICompletableSynchronizedStorageSession)storageSession).CompleteAsync(); Assert.That(firstOperation.WasDisposed, Is.False); Assert.That(firstOperation.WasApplied, Is.False); Assert.That(secondOperation.WasDisposed, Is.False); Assert.That(secondOperation.WasApplied, Is.False); }
public Task Configure(CancellationToken cancellationToken = default) { // with this we have a partition key per run which makes things naturally isolated partitionKey = Guid.NewGuid().ToString(); var serializer = new JsonSerializer { ContractResolver = new UpperCaseIdIntoLowerCaseIdContractResolver(), Converters = { new ReadOnlyMemoryConverter() } }; var persistenceConfiguration = (PersistenceConfiguration)Variant.Values[0]; var sagaPersistenceConfiguration = new SagaPersistenceConfiguration(); if (persistenceConfiguration.UsePessimisticLocking) { var pessimisticLockingConfiguration = sagaPersistenceConfiguration.UsePessimisticLocking(); if (SessionTimeout.HasValue) { pessimisticLockingConfiguration.SetLeaseLockAcquisitionTimeout(SessionTimeout.Value); } SupportsPessimisticConcurrency = true; } var partitionKeyPath = new PartitionKeyPath(SetupFixture.PartitionPathKey); var resolver = new ContainerHolderResolver(this, new ContainerInformation(SetupFixture.ContainerName, partitionKeyPath), SetupFixture.DatabaseName); SynchronizedStorage = new StorageSessionFactory(resolver, null); SagaStorage = new SagaPersister(serializer, sagaPersistenceConfiguration); OutboxStorage = new OutboxPersister(resolver, serializer, OutboxTimeToLiveInSeconds); GetContextBagForSagaStorage = () => { var contextBag = new ContextBag(); // This populates the partition key required to participate in a shared transaction var setAsDispatchedHolder = new SetAsDispatchedHolder { PartitionKey = new PartitionKey(partitionKey), ContainerHolder = resolver.ResolveAndSetIfAvailable(contextBag) }; contextBag.Set(setAsDispatchedHolder); contextBag.Set(new PartitionKey(partitionKey)); return(contextBag); }; GetContextBagForOutbox = () => { var contextBag = new ContextBag(); // This populates the partition key required to participate in a shared transaction var setAsDispatchedHolder = new SetAsDispatchedHolder { PartitionKey = new PartitionKey(partitionKey), ContainerHolder = resolver.ResolveAndSetIfAvailable(contextBag) }; contextBag.Set(setAsDispatchedHolder); contextBag.Set(new PartitionKey(partitionKey)); return(contextBag); }; return(Task.CompletedTask); }