/// <summary> /// Migrate events from the source stream to this migration stream. /// </summary> /// <param name="source"> /// Events are read from this stream. /// </param> /// <param name="migrator"> /// Function invoked to migrate an event. Receives the event and /// its sequence number as argument, must return the event to be /// written (or null if the event should be dropped). /// /// The function will be invoked starting with the very first /// event in the source stream, but its return value will be /// ignored if the sequence number is smaller than the value /// returned by <see cref="LastWrittenAsync"/> (the purpose of /// this is to let the migrator function accumulate any necessary /// internal state). /// /// The events retain their sequence number. Dropped events (those /// for which the migrator returns null) are not migrated at all. /// </param> /// <param name="refreshDelay"> /// No effect until migration has reached the end of the source /// stream. /// /// If null, migration ends when the end of the source stream is /// reached. /// /// If provided, once the end of the source stream is /// reached, will poll the source stream forever (with this delay) /// looking for new events. /// </param> /// <param name="cancel"> /// Invoke to interrupt the migration. /// </param> public async Task MigrateFromAsync( IEventStream <TEvent> source, Func <TEvent, uint, TEvent> migrator, TimeSpan?refreshDelay, CancellationToken cancel) { var lastSeq = await LastWrittenAsync(cancel); var list = new List <KeyValuePair <uint, TEvent> >(); while (true) { cancel.ThrowIfCancellationRequested(); var fetch = source.BackgroundFetchAsync(); while (source.TryGetNext() is TEvent next) { var seq = source.Sequence; if (!(migrator(next, seq) is TEvent migrated)) { // Do not migrate this event. continue; } if (seq <= lastSeq) { // Event already migrated previously continue; } list.Add(new KeyValuePair <uint, TEvent>(seq, migrated)); } if (list.Count > 0) { await WriteAsync(list, cancel).ConfigureAwait(false); list.Clear(); } var more = await fetch.ConfigureAwait(false); if (more()) { continue; } // Reached the end of the stream. if (refreshDelay == null) { return; } else { await Task.Delay(refreshDelay.Value, cancel).ConfigureAwait(false); } } }
/// <summary> /// Attempts to fetch events from the remote stream, making them available to /// <see cref="IEventStream{TEvent}.TryGetNext"/>. /// </summary> /// <remarks> /// Will always fetch at least one event, if events are available. The actual number /// depends on multiple optimization factors. /// /// Calling this function adds events to an internal cache, which may grow out of /// control unless you call <see cref="IEventStream{TEvent}.TryGetNext"/> regularly. /// /// If no events are available on the remote stream, returns false. /// </remarks> public static async Task <bool> FetchAsync(this IEventStream stream, CancellationToken cancel = default(CancellationToken)) { var commit = await stream.BackgroundFetchAsync(cancel); return(commit()); }