/// <summary> /// Schedules the specified command. /// </summary> /// <param name="scheduledCommand">The scheduled command.</param> /// <returns> /// A task that is complete when the command has been successfully scheduled. /// </returns> /// <exception cref="System.NotSupportedException">Non-immediate scheduling is not supported.</exception> public virtual async Task Schedule(IScheduledCommand <TAggregate> scheduledCommand) { if (scheduledCommand.Result is CommandDeduplicated) { return; } if (scheduledCommand.Command.CanBeDeliveredDuringScheduling() && scheduledCommand.IsDue()) { if (!await PreconditionHasBeenMet(scheduledCommand)) { CommandScheduler.DeliverIfPreconditionIsMetSoon( scheduledCommand, Configuration.Current); } else { // resolve the command scheduler so that delivery goes through the whole pipeline await Configuration.Current.CommandScheduler <TAggregate>().Deliver(scheduledCommand); return; } } if (scheduledCommand.Result == null) { throw new NotSupportedException("Deferred scheduling is not supported by the current command scheduler pipeline configuration."); } }
public async Task Schedule(IScheduledCommand <TAggregate> scheduledCommand) { var storedScheduledCommand = await Storage.StoreScheduledCommand( scheduledCommand, createCommandSchedulerDbContext, (scheduledCommandEvent1, db) => ClockNameForEvent(this, scheduledCommandEvent1, db)); Activity.OnNext(new CommandScheduled(scheduledCommand) { ClockName = storedScheduledCommand.Clock.Name }); // deliver the command immediately if appropriate if (scheduledCommand.IsDue(storedScheduledCommand.Clock)) { // sometimes the command depends on a precondition event that hasn't been saved if (!await commandPreconditionVerifier.IsPreconditionSatisfied(scheduledCommand)) { this.DeliverIfPreconditionIsSatisfiedWithin( TimeSpan.FromSeconds(10), scheduledCommand, eventBus); } else { var scheduler = Configuration.Current.CommandScheduler <TAggregate>(); await scheduler.Deliver(scheduledCommand); } } }
internal static async Task <ScheduledCommand> StoreScheduledCommand <TAggregate>( IScheduledCommand <TAggregate> scheduledCommand, Func <CommandSchedulerDbContext> createDbContext, Func <IScheduledCommand <TAggregate>, CommandSchedulerDbContext, Task <string> > clockNameForEvent) where TAggregate : class { ScheduledCommand storedScheduledCommand; using (var db = createDbContext()) { var domainTime = Domain.Clock.Now(); // get or create a clock to schedule the command on var clock = scheduledCommand.Clock as Clock; if (clock == null) { var clockName = await clockNameForEvent(scheduledCommand, db); var clockStartTime = scheduledCommand.Clock?.Now() ?? domainTime; clock = await GetOrAddSchedulerClock( db, clockName, clockStartTime); } storedScheduledCommand = CreateStoredScheduledCommand( scheduledCommand, domainTime, clock); if (scheduledCommand.IsDue() && !scheduledCommand.Command.RequiresDurableScheduling()) { storedScheduledCommand.NonDurable = true; return(storedScheduledCommand); } Debug.WriteLine( $"Storing command '{scheduledCommand.Command.CommandName}' ({scheduledCommand.TargetId}:{storedScheduledCommand.SequenceNumber}) on clock '{clock.Name}'"); await SaveScheduledCommandToDatabase(db, storedScheduledCommand, scheduledCommand); } scheduledCommand.IfTypeIs <ScheduledCommand <TAggregate> >() .ThenDo(c => c.SequenceNumber = storedScheduledCommand.SequenceNumber); return(storedScheduledCommand); }
/// <summary> /// Schedules the specified command. /// </summary> /// <param name="scheduledCommand">The scheduled command.</param> /// <returns> /// A task that is complete when the command has been successfully scheduled. /// </returns> /// <exception cref="System.NotSupportedException">Non-immediate scheduling is not supported.</exception> public virtual async Task Schedule(IScheduledCommand <TAggregate> scheduledCommand) { if (scheduledCommand.IsDue() && await VerifyPrecondition(scheduledCommand)) { // resolve the command scheduler so that delivery goes through the whole pipeline await Configuration.Current.CommandScheduler <TAggregate>().Deliver(scheduledCommand); return; } if (scheduledCommand.Result == null) { throw new NotSupportedException("Deferred scheduling is not supported."); } }
private async Task Schedule(IScheduledCommand <TAggregate> cmd, Func <IScheduledCommand <TAggregate>, Task> next) { var storedCommand = await Storage.StoreScheduledCommand( cmd, createDbContext, GetClockName); if (cmd.IsDue(storedCommand.Clock)) { var preconditionVerifier = Configuration.Current.Container.Resolve <ICommandPreconditionVerifier>(); // sometimes the command depends on a precondition event that hasn't been saved if (await preconditionVerifier.IsPreconditionSatisfied(cmd)) { await Configuration.Current.CommandScheduler <TAggregate>().Deliver(cmd); } } await next(cmd); }
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); }