/// <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);
        }
Exemple #2
0
        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;
            }
        }
Exemple #4
0
 public void Warning(string message, Exception ex = null)
 {
     _log.Warning(Elapsed + " " + message, ex);
 }
Exemple #5
0
 /// <inheritdoc/>
 public static void Warning(object caller, object message, params object[] replacements)
 => _log.Warning(caller, message, replacements);