private void CreateClockIfNotExists() { try { using (var con = new SqlConnection(TestSettings.ConnectionStringCommandScheduler)) { con.Open(); using ( var command = new SqlCommand( $"select 1 from [Scheduler].[Clock] where [Name]='{TestSettings.CustomClockName}'", con)) { var exists = command.ExecuteScalar(); if (exists == null) { _configuration .SchedulerClockRepository() .CreateClock(TestSettings.CustomClockName, Clock.Now()); } } } } catch (Exception e) { Console.WriteLine(e); } }
public async Task When_ServiceBusCommandQueueSender_is_subscribed_to_the_service_bus_then_messages_are_scheduled_to_trigger_event_based_scheduled_commands() { VirtualClock.Start(DateTimeOffset.Now.AddHours(-13)); using (var queueReceiver = CreateQueueReceiver()) { var aggregateIds = Enumerable.Range(1, 5) .Select(_ => Guid.NewGuid()) .ToArray(); aggregateIds.ForEach(async id => { var order = CommandSchedulingTests_EventSourced.CreateOrder(orderId: id); // due enough in the future that the scheduler won't apply the commands immediately var due = Clock.Now().AddSeconds(5); order.Apply(new ShipOn(due)); Console.WriteLine(new { ShipOrderId = order.Id, due }); await Configuration.Current.Repository <Order>().Save(order); }); // reset the clock so that when the messages are delivered, the target commands are now due Clock.Reset(); await queueReceiver.StartReceivingMessages(); schedulerActivity .Select(a => Guid.Parse(a.TargetId)) .ShouldBeEquivalentTo(aggregateIds); } }
/// <summary> /// Initializes a new instance of the <see cref="Event" /> class. /// </summary> protected Event() { var command = CommandContext.Current?.Command; if (command != null) { this.SetActor(command); ETag = command.ETag; } Timestamp = Clock.Now(); }
public async Task When_a_command_has_been_completed_and_a_message_for_it_arrives_the_message_is_also_Completed() { var aggregateId = Any.Guid(); // due in the past so that it's scheduled immediately var order = CommandSchedulingTests_EventSourced.CreateOrder(orderId: aggregateId) .Apply(new ShipOn(Clock.Now().AddSeconds(-5))); queueSender.MessageDeliveryOffsetFromCommandDueTime = TimeSpan.FromSeconds(0); await Configuration.Current.Repository <Order>().Save(order); using (var receiver = CreateQueueReceiver()) { var receivedMessages = new List <IScheduledCommand>(); receiver.Messages .Where(m => m.IfTypeIs <IScheduledCommand <Order> >() .Then(c => c.TargetId == aggregateId.ToString()) .ElseDefault()) .Subscribe(receivedMessages.Add); await receiver.StartReceivingMessages(); await Task.Delay(TimeSpan.FromSeconds(5)); receivedMessages.Should() .ContainSingle(m => m.IfTypeIs <IScheduledCommand <Order> >() .Then(c => c.TargetId == aggregateId.ToString()) .ElseDefault()); } using (var receiver = CreateQueueReceiver()) { var receivedMessages = new List <IScheduledCommand>(); receiver.Messages .Where(m => m.IfTypeIs <IScheduledCommand <Order> >() .Then(c => c.TargetId == aggregateId.ToString()) .ElseDefault()) .Subscribe(receivedMessages.Add); await receiver.StartReceivingMessages(); await Task.Delay(TimeSpan.FromSeconds(10)); receivedMessages.Count.Should().Be(0); } }
public async Task When_a_command_has_been_completed_and_a_message_for_it_arrives_the_message_is_also_Completed() { var aggregateId = Any.Guid(); // due in the past so that it's scheduled immediately var order = CommandSchedulingTests.CreateOrder(orderId: aggregateId) .Apply(new ShipOn(Clock.Now().AddSeconds(-5))); queueSender.MessageDeliveryOffsetFromCommandDueTime = TimeSpan.FromSeconds(0); await orderRepository.Save(order); await RunCatchup(); using (var receiver = CreateQueueReceiver()) { var receivedMessages = new List <IScheduledCommand>(); receiver.Messages .Where(c => c.AggregateId == aggregateId) .Subscribe(receivedMessages.Add); await receiver.StartReceivingMessages(); await Task.Delay(TimeSpan.FromSeconds(5)); receivedMessages.Should().ContainSingle(e => e.AggregateId == aggregateId); } using (var receiver = CreateQueueReceiver()) { var receivedMessages = new List <IScheduledCommand>(); receiver.Messages .Where(c => c.AggregateId == aggregateId) .Subscribe(receivedMessages.Add); await receiver.StartReceivingMessages(); await Task.Delay(TimeSpan.FromSeconds(10)); receivedMessages.Count().Should().Be(0); } }
public async Task When_a_command_trigger_message_arrives_early_it_is_not_Completed() { VirtualClock.Start(Clock.Now().AddHours(-1)); var aggregateId = Any.Guid(); var appliedCommands = new List <ICommandSchedulerActivity>(); scheduler.Activity .Where(c => c.ScheduledCommand.AggregateId == aggregateId) .Subscribe(appliedCommands.Add); using (var receiver = CreateQueueReceiver()) { await receiver.StartReceivingMessages(); // due enough in the future that the scheduler won't apply the commands immediately var order = await CommandSchedulingTests.CreateOrder(orderId : aggregateId) .ApplyAsync(new ShipOn(Clock.Now().AddMinutes(2))); await orderRepository.Save(order); await RunCatchup(); await receiver.Messages .FirstAsync(c => c.AggregateId == aggregateId) .Timeout(TimeSpan.FromMinutes(1)); await Task.Delay(1000); appliedCommands.Should().BeEmpty(); } Clock.Reset(); using (var receiver = CreateQueueReceiver()) { await receiver.StartReceivingMessages(); await Task.Delay(1000); appliedCommands.Should().Contain(c => c.ScheduledCommand.AggregateId == aggregateId); } }
/// <summary> /// Saves a snapshot of the aggregate. /// </summary> public static async Task SaveSnapshot <TAggregate>( this ISnapshotRepository repository, TAggregate aggregate) where TAggregate : class, IEventSourced { var snapshotCreator = Configuration.Current.Container.Resolve <ICreateSnapshot <TAggregate> >(); var snapshot = snapshotCreator.CreateSnapshot(aggregate); snapshot.AggregateId = aggregate.Id; snapshot.AggregateTypeName = AggregateType <TAggregate> .EventStreamName; snapshot.LastUpdated = Clock.Now(); snapshot.Version = aggregate.Version; snapshot.ETags = aggregate.ETags() .Where(e => !string.IsNullOrWhiteSpace(e)) .ToArray(); await repository.SaveSnapshot(snapshot); }
public async Task When_ServiceBusCommandQueueSender_is_subscribed_to_the_service_bus_then_messages_are_scheduled_to_trigger_event_based_scheduled_commands() { VirtualClock.Start(DateTimeOffset.Now.AddHours(-13)); using (var queueReceiver = CreateQueueReceiver()) { var aggregateIds = Enumerable.Range(1, 5) .Select(_ => Guid.NewGuid()) .ToArray(); aggregateIds.ForEach(id => { var order = CommandSchedulingTests.CreateOrder(orderId: id); // due enough in the future that the scheduler won't apply the commands immediately var due = Clock.Now().AddSeconds(5); order.Apply(new ShipOn(due)); Console.WriteLine(new { ShipOrderId = order.Id, due }); orderRepository.Save(order); }); RunCatchup(); // reset the clock so that when the messages are delivered, the target commands are now due Clock.Reset(); queueReceiver.StartReceivingMessages(); var activity = await scheduler.Activity .Where(a => aggregateIds.Contains(a.ScheduledCommand.AggregateId)) .Take(5) .ToList() .Timeout(TimeSpan.FromMinutes(5)); activity.Select(a => a.ScheduledCommand.AggregateId) .ShouldBeEquivalentTo(aggregateIds); } }
public async Task When_a_command_trigger_message_arrives_early_it_is_not_Completed() { VirtualClock.Start(Clock.Now().AddHours(-1)); var aggregateId = Any.Guid(); var appliedCommands = new List <ICommandSchedulerActivity>(); using (var receiver = CreateQueueReceiver()) { await receiver.StartReceivingMessages(); // due enough in the future that the scheduler won't apply the commands immediately var order = await CommandSchedulingTests_EventSourced.CreateOrder(orderId : aggregateId) .ApplyAsync(new ShipOn(Clock.Now().AddMinutes(2))); await Configuration.Current.Repository <Order>().Save(order); await receiver.Messages .OfType <IScheduledCommand <Order> >() .FirstAsync(c => c.TargetId == aggregateId.ToString()) .Timeout(TimeSpan.FromMinutes(1)); await Task.Delay(1000); appliedCommands.Should().BeEmpty(); } Clock.Reset(); using (var receiver = CreateQueueReceiver()) { await receiver.StartReceivingMessages(); await Task.Delay(1000); // FIX: (When_a_command_trigger_message_arrives_early_it_is_not_Completed) how was this even passing? // appliedCommands.Should().Contain(c => c.ScheduledCommand.AggregateId == aggregateId); } }
/// <summary> /// Initializes the interface properties of a snapshot. /// </summary> /// <typeparam name="TAggregate">The type of the aggregate.</typeparam> /// <param name="aggregate">The aggregate.</param> /// <param name="snapshot">The snapshot.</param> /// <exception cref="System.ArgumentNullException"> /// aggregate /// or /// snapshot /// </exception> internal static void InitializeSnapshot <TAggregate>(this TAggregate aggregate, ISnapshot snapshot) where TAggregate : class, IEventSourced { if (aggregate == null) { throw new ArgumentNullException(nameof(aggregate)); } if (snapshot == null) { throw new ArgumentNullException(nameof(snapshot)); } if (aggregate.PendingEvents.Any()) { throw new InvalidOperationException("A snapshot can only be created from an aggregate having no pending events. Save the aggregate before creating a snapshot."); } snapshot.AggregateId = aggregate.Id; snapshot.AggregateTypeName = AggregateType <TAggregate> .EventStreamName; snapshot.LastUpdated = Clock.Now(); snapshot.Version = aggregate.Version; snapshot.ETags = aggregate.CreateETagBloomFilter(); }
/// <summary> /// Writes trace information during command scheduling and delivery for all aggregate types. If no delegates are specified, then output is written to <see cref="System.Diagnostics.Trace" /> on all events. /// </summary> /// <param name="configuration">The domain configuration.</param> /// <param name="onScheduling">An optional delegate to trace information about a command before calling Schedule on the inner scheduler.</param> /// <param name="onScheduled">An optional delegate to trace information about a command after calling Schedule on the inner scheduler.</param> /// <param name="onDelivering">An optional delegate to trace information about a command before calling Deliver on the inner scheduler.</param> /// <param name="onDelivered">An optional delegate to trace information about a command after calling Deliver on the inner scheduler.</param> /// <returns>The same configuration object.</returns> public static Configuration TraceScheduledCommands( this Configuration configuration, Action <IScheduledCommand> onScheduling = null, Action <IScheduledCommand> onScheduled = null, Action <IScheduledCommand> onDelivering = null, Action <IScheduledCommand> onDelivered = null) { var traceInitializer = configuration.Container .Resolve <CommandSchedulerPipelineTraceInitializer>(); if (onScheduling == null && onScheduled == null && onDelivering == null && onDelivered == null) { onScheduling = cmd => Trace.WriteLine($"[Scheduling] @{Clock.Now()}: {cmd}"); onScheduled = cmd => Trace.WriteLine($"[Scheduled] @{Clock.Now()}: {cmd}"); onDelivering = cmd => Trace.WriteLine($"[Delivering] @{Clock.Now()}: {cmd}"); onDelivered = cmd => Trace.WriteLine($"[Delivered] @{Clock.Now()}: {cmd}"); } traceInitializer.OnScheduling(onScheduling); traceInitializer.OnScheduled(onScheduled); traceInitializer.OnDelivering(onDelivering); traceInitializer.OnDelivered(onDelivered); traceInitializer.Initialize(configuration); return(configuration); }