private static ScheduledCommand CreateStoredScheduledCommand <TAggregate>( IScheduledCommand <TAggregate> scheduledCommand, DateTimeOffset domainTime, Clock schedulerClock) where TAggregate : class => new ScheduledCommand { AggregateId = ScheduledCommand <TAggregate> .TargetGuid(scheduledCommand), SequenceNumber = scheduledCommand .IfTypeIs <IEvent>() .Then(e => e.SequenceNumber) .Else(() => - DateTimeOffset.UtcNow.Ticks), AggregateType = Command.TargetNameFor(scheduledCommand.Command.GetType()), SerializedCommand = scheduledCommand.ToJson(), CreatedTime = domainTime, DueTime = scheduledCommand.DueTime ?? schedulerClock.Now(), Clock = schedulerClock };
private async Task Enqueue(IScheduledCommand scheduledCommand) { var message = new BrokeredMessage(scheduledCommand.ToJson()) { SessionId = scheduledCommand.AggregateId.ToString() }; if (scheduledCommand.DueTime != null) { message.ScheduledEnqueueTimeUtc = scheduledCommand.DueTime.Value.UtcDateTime.Add(MessageDeliveryOffsetFromCommandDueTime); } messageSubject.OnNext(scheduledCommand); using (new TransactionScope(TransactionScopeOption.Suppress)) { await queueClient.SendAsync(message); } }
private async Task <ScheduledCommand> StoreScheduledCommand(IScheduledCommand <TAggregate> scheduledCommandEvent) { ScheduledCommand storedScheduledCommand; using (var db = createCommandSchedulerDbContext()) { var domainTime = Domain.Clock.Now(); // store the scheduled command var clockName = await ClockNameForEvent(scheduledCommandEvent, db); var schedulerClock = await db.Clocks.SingleOrDefaultAsync(c => c.Name == clockName); if (schedulerClock == null) { Debug.WriteLine(string.Format("SqlCommandScheduler: Creating clock '{0}' @ {1}", clockName, domainTime)); schedulerClock = new Clock { Name = clockName, UtcNow = domainTime, StartTime = domainTime }; db.Clocks.Add(schedulerClock); await db.SaveChangesAsync(); } storedScheduledCommand = new ScheduledCommand { AggregateId = scheduledCommandEvent.AggregateId, SequenceNumber = scheduledCommandEvent.SequenceNumber, AggregateType = eventStreamName, SerializedCommand = scheduledCommandEvent.ToJson(), CreatedTime = domainTime, DueTime = scheduledCommandEvent.DueTime, Clock = schedulerClock }; if (storedScheduledCommand.ShouldBeDeliveredImmediately() && !scheduledCommandEvent.Command.RequiresDurableScheduling) { storedScheduledCommand.NonDurable = true; return(storedScheduledCommand); } Debug.WriteLine(string.Format("SqlCommandScheduler: Storing command '{0}' ({1}:{2}) on clock '{3}'", scheduledCommandEvent.Command.CommandName, scheduledCommandEvent.AggregateId, scheduledCommandEvent.SequenceNumber, clockName)); db.ScheduledCommands.Add(storedScheduledCommand); while (true) { try { db.SaveChanges(); break; } catch (DbUpdateException exception) { if (exception.IsConcurrencyException()) { if (storedScheduledCommand.SequenceNumber < 0) { // for scheduler-assigned sequence numbers, decrement and retry storedScheduledCommand.SequenceNumber--; } else { // this is not a scheduler-assigned sequence number, so the concurrency exception indicates an actual issue break; } } else { throw; } } } } return(storedScheduledCommand); }
internal static async Task <ScheduledCommand> StoreScheduledCommand <TAggregate>( IScheduledCommand <TAggregate> scheduledCommand, Func <CommandSchedulerDbContext> createDbContext, Func <IScheduledCommand <TAggregate>, CommandSchedulerDbContext, Task <string> > clockNameForEvent) where TAggregate : class, IEventSourced { ScheduledCommand storedScheduledCommand; using (var db = createDbContext()) { var domainTime = Domain.Clock.Now(); // get or create a clock to schedule the command on var clockName = await clockNameForEvent(scheduledCommand, db); var schedulerClock = await db.Clocks.SingleOrDefaultAsync(c => c.Name == clockName); if (schedulerClock == null) { Debug.WriteLine(String.Format("Creating clock '{0}' @ {1}", clockName, domainTime)); schedulerClock = new Clock { Name = clockName, UtcNow = domainTime, StartTime = domainTime }; db.Clocks.Add(schedulerClock); await db.SaveChangesAsync(); } storedScheduledCommand = new ScheduledCommand { AggregateId = scheduledCommand.AggregateId, SequenceNumber = scheduledCommand.SequenceNumber, AggregateType = AggregateType <TAggregate> .EventStreamName, SerializedCommand = scheduledCommand.ToJson(), CreatedTime = domainTime, DueTime = scheduledCommand.DueTime, Clock = schedulerClock }; if (scheduledCommand.IsDue(storedScheduledCommand.Clock) && !scheduledCommand.Command.RequiresDurableScheduling) { storedScheduledCommand.NonDurable = true; return(storedScheduledCommand); } Debug.WriteLine(String.Format("Storing command '{0}' ({1}:{2}) on clock '{3}'", scheduledCommand.Command.CommandName, scheduledCommand.AggregateId, scheduledCommand.SequenceNumber, clockName)); db.ScheduledCommands.Add(storedScheduledCommand); while (true) { try { db.SaveChanges(); scheduledCommand.Result = new CommandScheduled(scheduledCommand) { ClockName = schedulerClock.Name }; break; } catch (DbUpdateException exception) { if (exception.IsConcurrencyException()) { if (storedScheduledCommand.SequenceNumber < 0) { // for scheduler-assigned sequence numbers, decrement and retry storedScheduledCommand.SequenceNumber--; } else { // this is not a scheduler-assigned sequence number, so the concurrency exception indicates an actual issue break; } } else { throw; } } } } return(storedScheduledCommand); }
private ScheduledCommand StoreScheduledCommand(IScheduledCommand <TAggregate> scheduledCommandEvent) { ScheduledCommand storedScheduledCommand; using (var db = createCommandSchedulerDbContext()) { var domainTime = Domain.Clock.Now(); // store the scheduled command var clockName = ClockNameForEvent(scheduledCommandEvent, db); var schedulerClock = db.Clocks.SingleOrDefault(c => c.Name == clockName); if (schedulerClock == null) { schedulerClock = new Clock { Name = clockName, UtcNow = domainTime, StartTime = domainTime }; db.Clocks.Add(schedulerClock); db.SaveChanges(); } storedScheduledCommand = new ScheduledCommand { AggregateId = scheduledCommandEvent.AggregateId, SequenceNumber = scheduledCommandEvent.SequenceNumber, AggregateType = eventStreamName, SerializedCommand = scheduledCommandEvent.ToJson(), CreatedTime = domainTime, DueTime = scheduledCommandEvent.DueTime, Clock = schedulerClock }; if (storedScheduledCommand.ShouldBeDeliveredImmediately() && !scheduledCommandEvent.Command.RequiresDurableScheduling) { storedScheduledCommand.NonDurable = true; return(storedScheduledCommand); } db.ScheduledCommands.Add(storedScheduledCommand); while (true) { try { db.SaveChanges(); break; } catch (DbUpdateException exception) { if (exception.IsConcurrencyException()) { if (storedScheduledCommand.SequenceNumber < 0) { // for scheduler-assigned sequence numbers, decrement and retry storedScheduledCommand.SequenceNumber--; } else { // this is not a scheduler-assigned sequence number, so the concurrency exception indicates break; } } else { throw; } } } } return(storedScheduledCommand); }
private async Task Enqueue(IScheduledCommand scheduledCommand) { var message = new BrokeredMessage(scheduledCommand.ToJson()) { SessionId = scheduledCommand.AggregateId.ToString() }; if (scheduledCommand.DueTime != null) { message.ScheduledEnqueueTimeUtc = scheduledCommand.DueTime.Value.UtcDateTime.Add(MessageDeliveryOffsetFromCommandDueTime); } messageSubject.OnNext(scheduledCommand); using (new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)) { await queueClient.SendAsync(message); } }