예제 #1
0
        /// <summary>
        /// Configures the system to use SQL-backed command scheduling.
        /// </summary>
        /// <param name="configuration">The configuration.</param>
        /// <returns>The updated configuration.</returns>
        public static Configuration UseSqlCommandScheduling(
            this Configuration configuration,
            Action <ReadModelCatchup <CommandSchedulerDbContext> > configureCatchup = null)
        {
            var container    = configuration.Container;
            var scheduler    = container.Resolve <SqlCommandScheduler>();
            var subscription = container.Resolve <IEventBus>().Subscribe(scheduler);

            configuration.RegisterForDisposal(subscription);
            container.RegisterSingle(c => scheduler);

            if (configureCatchup != null)
            {
                var catchup = new ReadModelCatchup <CommandSchedulerDbContext>(scheduler)
                {
                    CreateReadModelDbContext = scheduler.CreateCommandSchedulerDbContext,
                };
                configureCatchup(catchup);
                catchup.PollEventStore();
                container.RegisterSingle(c => catchup);
                configuration.RegisterForDisposal(catchup);
            }

            return(configuration);
        }
예제 #2
0
 /// <summary>
 /// Polls the event store and passes events to <see cref="ReadModelCatchup{TDbContext}.Run" /> when new events are found.
 /// </summary>
 /// <typeparam name="TDbContext">The type of the database context used to access the read models.</typeparam>
 /// <param name="readModelCatchup">The read model catchup instance to run.</param>
 /// <param name="interval">The interval at which polling occurs.</param>
 /// <param name="scheduler">The scheduler on which work is scheduled.</param>
 public static ReadModelCatchup <TDbContext> PollEventStore <TDbContext>(
     this ReadModelCatchup <TDbContext> readModelCatchup,
     TimeSpan?interval    = null,
     IScheduler scheduler = null)
     where TDbContext : DbContext
 {
     interval  = interval ?? TimeSpan.FromSeconds(5);
     scheduler = scheduler ?? Scheduler.Default;
     return(readModelCatchup.PollEventStore(Observable.Interval(interval.Value, scheduler).Select(_ => Unit.Default)));
 }
예제 #3
0
        /// <summary>
        /// Configures the system to use SQL-backed command scheduling.
        /// </summary>
        /// <param name="configuration">The configuration.</param>
        /// <returns>The updated configuration.</returns>
        public static Configuration UseSqlCommandScheduling(
            this Configuration configuration,
            Action <ReadModelCatchup <CommandSchedulerDbContext> > configureCatchup = null)
        {
            var container = configuration.Container;

            container.AddFallbackToDefaultClock();

            var scheduler = new SqlCommandScheduler(
                configuration,
                container.Resolve <Func <CommandSchedulerDbContext> >(),
                container.Resolve <GetClockName>());

            if (container.All(r => r.Key != typeof(SqlCommandScheduler)))
            {
                container.Register(c => scheduler)
                .Register <ISchedulerClockTrigger>(c => scheduler)
                .Register <ISchedulerClockRepository>(c => scheduler);
            }

            var subscription = container.Resolve <IEventBus>().Subscribe(scheduler);

            configuration.RegisterForDisposal(subscription);
            container.RegisterSingle(c => scheduler);

            if (configureCatchup != null)
            {
                var catchup = new ReadModelCatchup <CommandSchedulerDbContext>(scheduler)
                {
                    CreateReadModelDbContext = scheduler.CreateCommandSchedulerDbContext
                };
                configureCatchup(catchup);
                catchup.PollEventStore();
                container.RegisterSingle(c => catchup);
                configuration.RegisterForDisposal(catchup);
            }

            configuration.IsUsingSqlCommandScheduling(true);

            return(configuration);
        }
예제 #4
0
        /// <summary>
        /// Polls the event store and passes events to <see cref="ReadModelCatchup{TDbContext}.Run" /> when new events are found.
        /// </summary>
        /// <typeparam name="TDbContext">The type of the database context used to access the read models.</typeparam>
        /// <param name="readModelCatchup">The read model catchup instance to run.</param>
        /// <param name="timer">An observable sequence that triggers event store polling.</param>
        public static ReadModelCatchup <TDbContext> PollEventStore <TDbContext>(
            this ReadModelCatchup <TDbContext> readModelCatchup,
            IObservable <Unit> timer)
            where TDbContext : DbContext
        {
            // produce a series of pings every time a) the timer elapses or b) a poll was just completed and there are still additional events to be processed
            // timer:                     o--------o--------o--------o--------o--------o--------o
            //                            |        |        |        |        |        |        |
            // previous run completed:    --------------------------------o----------------------
            //                            |        |        |        |    |   |        |        |
            // merged:                    o--------o--------o--------o----o---o--------o--------o
            IObservable <Unit> pollingPings = readModelCatchup
                                              .Progress
                                              .Where(status =>
            {
                // should we poll again? polling again immediately reduces potential latency by not waiting until the interval-baed polling happens again.
                if (status.IsEndOfBatch)
                {
                    // dispose any previous catchup query immediately, to release its app lock and dispose database connections
                    if (status.BatchCount > 0)
                    {
                        using (var eventStore = readModelCatchup.CreateOpenEventStoreDbContext().Result)
                        {
                            if (eventStore.Events.Max(e => e.Id) > status.CurrentEventId)
                            {
                                return(true);
                            }
                        }
                    }
                }

                return(false);
            })
                                              .Select(_ => Unit.Default)
                                              .Merge(timer);

            readModelCatchup.RunWhen(pollingPings);

            return(readModelCatchup);
        }
예제 #5
0
        /// <summary>
        /// Runs or observes a single catchup batch asynchronously.
        /// </summary>
        /// <typeparam name="TDbContext">The type of the database context.</typeparam>
        /// <param name="catchup">The read model catchup.</param>
        /// <param name="scheduler">A scheduler on which to schedule the catchup run. If none is specified, the default scheduler is used.</param>
        /// <returns>An observable of the catchup batch's progress, whether it was triggered by the caller or was already running.</returns>
        public static IObservable <ReadModelCatchupStatus> SingleBatchAsync <TDbContext>(
            this ReadModelCatchup <TDbContext> catchup,
            IScheduler scheduler = null)
            where TDbContext : DbContext
        {
            scheduler = scheduler ?? Scheduler.Default;

            return(Observable.Create <ReadModelCatchupStatus>(observer =>
            {
                var subscription = catchup.Progress.Subscribe(observer);

                var disposables = new CompositeDisposable {
                    subscription
                };

                return scheduler.ScheduleAsync(async(sched, token) =>
                {
                    if (await catchup.Run() == ReadModelCatchupResult.CatchupAlreadyInProgress)
                    {
                        // monitor for the end of the batch
                        var currentBatch = catchup.Progress
                                           .TakeWhile(s => !s.IsEndOfBatch)
                                           .Subscribe(onNext: s => { },
                                                      onCompleted: observer.OnCompleted,
                                                      onError: e => observer.OnCompleted());

                        disposables.Add(currentBatch);
                    }
                    else
                    {
                        observer.OnCompleted();
                    }

                    return Task.FromResult(disposables);
                });
            }));
        }