/// <summary> Catch up with locally stored data, without remote fetches. </summary> /// <returns> /// The number of events that have been passed to the projection. /// </returns> private uint CatchUpLocal() { var caughtUpWithProjection = false; var eventsPassedToProjection = 0u; while (true) { TEvent nextEvent; try { // This might throw due to serialization error // (but not for other reasons) nextEvent = Stream.TryGetNext(); } catch (Exception ex) { _log?.Warning($"[ES read] unreadable event at seq {Stream.Sequence}.", ex); _projection.SetPossiblyInconsistent(); Quarantine.Add(Stream.Sequence, ex); continue; } // No more local events left if (nextEvent == null) { break; } var seq = Stream.Sequence; if (_log != null && seq % 1000 == 0) { _log.Info($"[ES read] processing event at seq {seq}."); } if (caughtUpWithProjection || seq > _projection.Sequence) { caughtUpWithProjection = true; try { ++eventsPassedToProjection; // This might throw due to event processing issues // by one or more projection components _projection.Apply(seq, nextEvent); } catch (Exception ex) { _log?.Warning($"[ES read] processing error on event at seq {seq}.", ex); _projection.SetPossiblyInconsistent(); Quarantine.Add(seq, nextEvent, ex); } } } return(eventsPassedToProjection); }
internal static async Task Catchup( IReifiedProjection projection, IEventStream stream, CancellationToken cancel = default, ILogAdapter log = null) { try { // Load project and discard events before that. log?.Info("[ES init] loading projections."); await projection.TryLoadAsync(cancel).ConfigureAwait(false); var catchUp = projection.Sequence + 1; log?.Info($"[ES init] advancing stream to seq {catchUp}."); var streamSequence = await stream.DiscardUpTo(catchUp, cancel).ConfigureAwait(false); if (cancel.IsCancellationRequested) { return; } if (streamSequence != projection.Sequence) { log?.Warning( $"[ES init] projection seq {projection.Sequence} not found in stream (max seq is {streamSequence}: resetting everything."); // Cache is apparently beyond the available sequence. Could happen in // development environments with non-persistent events but persistent // caches. Treat cache as invalid and start from the beginning. stream.Reset(); projection.Reset(); } } catch (Exception e) { log?.Warning("[ES init] error while reading cache.", e); // Something went wrong when reading the cache. Stop. stream.Reset(); projection.Reset(); } }
/// <summary> Apply the specified event to the state. </summary> /// <remarks> The sequence number must be greater than <see cref="Sequence"/>. </remarks> public void Apply(uint seq, TEvent e) { if (seq <= Sequence) { throw new ArgumentException($"Event seq {seq} applied after seq {Sequence}", nameof(seq)); } // Always update sequence, even if update fails. Sequence = seq; try { var newState = _projection.Apply(seq, e, Current); if (newState == null) { throw new InvalidOperationException("Event generated a null state."); } Current = newState; } catch (Exception ex) { _log?.Warning($"[{Name}] error at seq {seq}", ex); _possiblyInconsistent = true; throw; } }
public void Warning(string message, Exception ex = null) { _log.Warning(Elapsed + " " + message, ex); }
/// <inheritdoc/> public static void Warning(object caller, object message, params object[] replacements) => _log.Warning(caller, message, replacements);