public void Nested_command_contexts_maintain_independent_token_sequences() { var sourceToken = Guid.NewGuid().ToString(); var outerCommand = new CommandDelivery <string>(""); var sequenceWithoutInnerContext = new List <string>(); var sequenceWithInnerContext = new List <string>(); using (var context = DeliveryContext.Establish(outerCommand)) { sequenceWithoutInnerContext.Add(context.NextToken(sourceToken)); sequenceWithoutInnerContext.Add(context.NextToken(sourceToken)); } using (var outerContext = DeliveryContext.Establish(outerCommand)) { sequenceWithInnerContext.Add(outerContext.NextToken(sourceToken)); using (var innerCtx = DeliveryContext.Establish( new CommandDelivery <object>( new object(), idempotencyToken: Guid.NewGuid().ToString()))) { innerCtx.NextToken(Guid.NewGuid().ToString()); } sequenceWithInnerContext.Add(outerContext.NextToken(sourceToken)); } sequenceWithInnerContext.Should().Equal(sequenceWithoutInnerContext); }
public void Command_objects_can_specify_idempotency_token_by_implementing_IIdempotent() { var command = new CreateCommandTarget(Guid.NewGuid().ToString()); var delivery1 = new CommandDelivery <CreateCommandTarget>(command); var delivery2 = new CommandDelivery <CreateCommandTarget>(command); delivery1.IdempotencyToken.Should().Be(delivery2.IdempotencyToken); }
public void When_a_retry_is_specified_then_the_original_due_time_is_unchanged() { var originalDueTime = DateTimeOffset.Parse("2018-01-01 12:00pm +00:00", CultureInfo.InvariantCulture); var delivery = new CommandDelivery <string>("hello", dueTime: originalDueTime); delivery.Retry(1.Days()); delivery .OriginalDueTime .Should() .Be(originalDueTime); }
public void When_a_ret_is_specified_and_the_due_time_was_null_then_the_new_due_time_is_calculated_from_the_current_clock_time() { using (var clock = VirtualClock.Start()) { var delivery = new CommandDelivery <string>( "hello", dueTime: null); delivery.Retry(1.Days()); delivery .DueTime .Should() .Be(clock.Now() + 1.Days()); } }
public async Task CommandReceiver_Trace_publishes_delivery_properties_as_telemetry_properties() { // arrange var dueTime = DateTimeOffset.Parse("2086-12-05", CultureInfo.InvariantCulture); var command = new CreateCommandTarget("the-id"); var handler = CommandHandler.Create <CreateCommandTarget>(d => d.Complete()); var delivery = new CommandDelivery <CreateCommandTarget>( command, idempotencyToken: "the-idempotency-token", dueTime: dueTime, numberOfPreviousAttempts: 4); await configuration.CommandScheduler <CreateCommandTarget>().Schedule(delivery); // act await configuration.CommandReceiver <CreateCommandTarget>().Receive(handler); // assert var logEvent = log[4]; logEvent .Category .Should() .Be("CommandReceiver<CreateCommandTarget>", "we're verifying that we have the right log event"); var properties = logEvent .Evaluate() .Properties; properties .Should() .Contain(t => t.Name == "IdempotencyToken" && t.Value.As <string>() == "the-idempotency-token"); properties .Should() .Contain(t => t.Name == "DueTime" && t.Value.As <DateTimeOffset>() == dueTime); properties .Should() .Contain(t => t.Name == "NumberOfPreviousAttempts" && t.Value.As <int>() == 4); }
public void Idempotency_tokens_are_generated_by_default_based_on_the_delivery_context_idempotency_token() { var delivery = new CommandDelivery <string>("hi", idempotencyToken: "the-original-idempotency-token"); string token1, token2; using (DeliveryContext.Establish(delivery)) { token1 = new CommandDelivery <string>("").IdempotencyToken; } using (DeliveryContext.Establish(delivery)) { token2 = new CommandDelivery <string>("").IdempotencyToken; } token2.Should().Be(token1); }
public static CommandDelivery <T> ToCommandDelivery <T>(this Message message) { var commandJson = Encoding.UTF8.GetString(message.Body); var command = commandJson.FromJsonTo <T>(); var enqueuedTimeUtc = message.SystemProperties.EnqueuedTimeUtc; var delivery = new CommandDelivery <T>( command, dueTime: enqueuedTimeUtc, originalDueTime: enqueuedTimeUtc, idempotencyToken: message.MessageId, numberOfPreviousAttempts: message.SystemProperties.DeliveryCount - 1); delivery.Properties["Message"] = message; return(delivery); }
public void The_default_retry_backoff_is_exponential() { var retries = Enumerable.Range(0, 5) .Select(i => { var delivery = new CommandDelivery <string>("hi", numberOfPreviousAttempts: i); return(delivery.Retry()); }); retries .Select(a => a.RetryPeriod) .ShouldBeEquivalentTo(new[] { 1.Minutes(), 4.Minutes(), 9.Minutes(), 16.Minutes(), 25.Minutes() }); }
public void Different_source_tokens_produce_different_next_tokens() { var token1 = Guid.NewGuid().ToString(); var token2 = Guid.NewGuid().ToString(); var sequence1 = new List <string>(); var sequence2 = new List <string>(); var command = new CommandDelivery <string>(""); using (var ctx = DeliveryContext.Establish(command)) { Enumerable.Range(1, 10).ToList().ForEach(_ => sequence1.Add(ctx.NextToken(token1))); } using (var ctx = DeliveryContext.Establish(command)) { Enumerable.Range(1, 10).ToList().ForEach(_ => sequence2.Add(ctx.NextToken(token2))); } sequence1.Should().NotIntersectWith(sequence2); }
public void CommandDelivery_Command_can_be_round_tripped_through_service_bus_message() { var original = new CommandDelivery <MyMessageClass>( new MyMessageClass( stringProperty: "oh hello", dateProperty: Clock.Now().Add(2.Days()), nullableDateProperty: Clock.Now().Add(3.Hours()), boolProperty: true, nullableBoolProperty: true, intProperty: 123, nullableIntProperty: 456), dueTime: Clock.Now().AddDays(1)); var message = original.ToMessage(); SimulateMessageHavingBeenReceived(message); var deserialized = message.ToCommandDelivery <MyMessageClass>(); deserialized.Command.ShouldBeEquivalentTo(original.Command); }
public async Task CommandScheduler_Trace_publishes_delivery_properties_as_telemetry_properties() { // arrange var scheduler = CommandScheduler.Create <CreateCommandTarget>(c => { }).Trace(); var delivery = new CommandDelivery <CreateCommandTarget>( new CreateCommandTarget("the-id"), idempotencyToken: "the-idempotency-token", dueTime: DateTimeOffset.Parse("2086-12-05", CultureInfo.InvariantCulture)); // act await scheduler.Schedule(delivery); // assert var logEvent = log[0]; logEvent .Category .Should() .Be("CommandScheduler<CreateCommandTarget>", "we're verifying that we have the right log event"); var properties = logEvent .Evaluate() .Properties; properties .Should() .Contain(t => t.Name == "IdempotencyToken" && t.Value.As <string>() == "the-idempotency-token"); properties .Should() .Contain(t => t.Name == "DueTime" && t.Value.As <DateTimeOffset>() == delivery.DueTime); }
public void CommandDelivery_properties_are_initialized_properly_from_a_received_Message() { using (VirtualClock.Start()) { var message = new CommandDelivery <string>( "hi", dueTime: Clock.Now()).ToMessage(); SimulateMessageHavingBeenReceived( message, enqueuedTimeUtc: Clock.Now().UtcDateTime); var deserialized = message.ToCommandDelivery <string>(); deserialized .DueTime .Value .UtcDateTime .ShouldBeEquivalentTo( message.SystemProperties .EnqueuedTimeUtc); deserialized .OriginalDueTime .Should() .Be( message.SystemProperties .EnqueuedTimeUtc); deserialized .NumberOfPreviousAttempts .Should() .Be( message .SystemProperties .DeliveryCount - 1); } }
private void SendCommandInternal <TCommand, TRequest, TResponse>(IComponentWriter writer, bool requireAuthority, ICommandDescriptor <TCommand, TRequest, TResponse> commandDescriptor, TRequest request, EntityId entityId, CommandCallback <TResponse> callback, TimeSpan?timeout, CommandDelivery commandDelivery) where TCommand : ICommandMetaclass, new() { Action sendAction = () => { var rawRequest = commandDescriptor.CreateRequest(request); Func <ICommandResponse <TCommand>, TResponse> extractResponse = rawResponse => ExtractResponse(commandDescriptor, rawResponse); var requestId = componentCommander.SendCommandInternal(entityId, rawRequest, extractResponse, callback, timeout, commandDelivery); TrackRequest(writer, requestId); }; SendGenericCommand(writer, requireAuthority, callback, sendAction); }
/// <summary> /// This method is required to prevent Unity compiler issues. /// </summary> private uint SendCommandRequest <TCommand>(EntityId entityId, ICommandRequest <TCommand> rawRequest, uint timeoutMs, CommandDelivery commandDelivery) where TCommand : ICommandMetaclass, new() { return(communicator.SendCommandRequest(entityId, rawRequest, timeoutMs, commandDelivery).Id); }
public Option <uint> SendCommandInternal <TCommand, TResponse>(EntityId entityId, ICommandRequest <TCommand> rawRequest, Func <ICommandResponse <TCommand>, TResponse> extractResponseFunc, CommandCallback <TResponse> callback, TimeSpan?timeout, CommandDelivery commandDelivery) where TCommand : ICommandMetaclass, new() { if (!commandResponseThunksRegistered.Contains(typeof(TCommand))) { var callbackKey = RegisterCommandResponse(extractResponseFunc); dispatcherCallbackKeys.Add(callbackKey); commandResponseThunksRegistered.Add(typeof(TCommand)); } Func <uint, uint> sendRequestWithTimeoutMs = timeoutMs => SendCommandRequest(entityId, rawRequest, timeoutMs, commandDelivery); return(SendGenericCommand(callback, sendRequestWithTimeoutMs, timeout)); }
public void SendCommand <TCommand, TRequest, TResponse>( ICommandDescriptor <TCommand, TRequest, TResponse> commandDescriptor, TRequest request, EntityId entityId, CommandCallback <TResponse> callback, TimeSpan?timeout = null, CommandDelivery commandDelivery = CommandDelivery.RoundTrip) where TCommand : ICommandMetaclass, new() { SendCommandInternal(null, SkipAuthorityCheck, commandDescriptor, request, entityId, callback, timeout, commandDelivery); }
/// <inheritdoc /> public ICommandResponseHandler <TResponse> SendCommand <TCommand, TRequest, TResponse>(IComponentWriter writer, ICommandDescriptor <TCommand, TRequest, TResponse> commandDescriptor, TRequest request, EntityId entityId, TimeSpan?timeout, CommandDelivery commandDelivery = CommandDelivery.RoundTrip) where TCommand : ICommandMetaclass, new() { return(CommandResponseHandler <TResponse> .Wrap(callback => SendCommandInternal(writer, PerformAuthorityCheck, commandDescriptor, request, entityId, callback, timeout, commandDelivery))); }
public void Deliveries_have_a_default_idempotency_token() { var delivery = new CommandDelivery <string>("hi"); delivery.IdempotencyToken.Should().NotBeNullOrWhiteSpace(); }
/// <inheritdoc /> public void SendCommand <TCommand, TResponse>(EntityId entityId, ICommandRequest <TCommand> rawRequest, Func <ICommandResponse <TCommand>, TResponse> extractResponseFunc, CommandCallback <TResponse> callback, TimeSpan?timeout = null, CommandDelivery commandDelivery = CommandDelivery.RoundTrip) where TCommand : ICommandMetaclass, new() { SendCommandInternal(entityId, rawRequest, extractResponseFunc, callback, timeout, commandDelivery); }
/// <inheritdoc /> public RequestId <OutgoingCommandRequest <TCommand> > SendCommandRequest <TCommand>(EntityId entityId, ICommandRequest <TCommand> request, Option <uint> timeout, CommandDelivery commandDelivery) where TCommand : ICommandMetaclass, new() { return(connection.SendCommandRequest(entityId, request, timeout, new CommandParameters { AllowShortCircuiting = commandDelivery == CommandDelivery.ShortCircuit })); }