/// <summary> /// Catch up with the stream (updating the state) until there are no new /// events available. /// </summary> public async Task CatchUpAsync(CancellationToken cancel = default) { Func <bool> finishFetch; // Local variable, to avoid reaching the limit when not doing the // initial catch-up. var eventsSinceLastCacheLoad = 0u; do { var fetchTask = Stream.BackgroundFetchAsync(cancel); // We have started fetching the next batch of events in // the background, so we might as well start processing // those we already have. This pattern is optimized for // when fetching events takes longer than processing them, // and remains safe (i.e. no runaway memory usage) when // the reverse is true. eventsSinceLastCacheLoad += CatchUpLocal(); // Maybe we have reached the event count limit before our // save/load cycle ? if (eventsSinceLastCacheLoad >= EventsBetweenCacheSaves) { eventsSinceLastCacheLoad = 0; var sw = Stopwatch.StartNew(); if (await _projection.TrySaveAsync(cancel)) { // Reset first, to release any used memory. _projection.Reset(); await _projection.TryLoadAsync(cancel); if (_projection.Sequence != Stream.Sequence) { throw new InvalidOperationException( "Projection Save/Load cycle failed to restore sequence."); } _log.Info($"[ES read] cache save/load cycle in {sw.Elapsed} at seq {_projection.Sequence}."); } } finishFetch = await fetchTask; } while (finishFetch()); // We reach this point if 1° all events cached in the stream have // been processed and 2° the fetch operation returned no new events NotifyRefresh(); }
/// <summary> Attempt to save the projection to the cache. </summary> /// <remarks> /// While this returns a task, the save operation itself does not touch the /// object (only an immutable copy of the state), so you do not need to /// wait for this task to finish before starting another operation. /// </remarks> public Task TrySaveAsync(CancellationToken cancel = default(CancellationToken)) { return(_projection.TrySaveAsync(cancel)); }
/// <summary> Attempt to save the projection to the cache. </summary> /// <remarks> /// While this returns a task, the save operation itself does not touch the /// object (only an immutable copy of the state), so you do not need to /// wait for this task to finish before starting another operation. /// </remarks> public Task TrySaveAsync(CancellationToken cancel = default) => _projection.TrySaveAsync(cancel);