public override void PreStart() { _callback = GetAsyncCallback <IReadOnlyList <IPersistentRepresentation> >(events => { if (events.Count == 0) { if (_currentSequenceNr > _toSequenceNr) { // end has been reached CompleteStage(); } else { switch (_state) { case State.NotifiedWhenQuerying: // maybe we missed some new event when querying, retry _state = State.Idle; Query(); break; case State.Querying: if (_live) { // nothing new, wait for notification _state = State.WaitingForNotification; } else { // not a live stream, nothing else currently in the database, close the stream CompleteStage(); } break; default: Log.Error($"Unexpected source state: {_state}"); FailStage(new IllegalStateException($"Unexpected source state: {_state}")); break; } } } else { var(evts, maxSequenceNr) = events.Aggregate((new List <EventEnvelope>(), _currentSequenceNr), (tuple, pr) => { if (!pr.IsDeleted && pr.SequenceNr >= _currentSequenceNr && pr.SequenceNr <= _toSequenceNr) { tuple.Item1.Add(new EventEnvelope(new Sequence(pr.SequenceNr), pr.PersistenceId, pr.SequenceNr, pr.Payload)); tuple.Item2 = pr.SequenceNr + 1; } else { tuple.Item2 = pr.SequenceNr + 1; } return(tuple); }); _currentSequenceNr = maxSequenceNr; Log.Debug($"Max sequence number is now {maxSequenceNr}"); if (evts.Count > 0) { evts.ForEach(_buffer.Enqueue); Deliver(); } else { // requery immediately _state = State.Idle; Query(); } } }); if (_live) { // subscribe to notification stream only if live stream was required var messageCallback = GetAsyncCallback <(RedisChannel channel, string bs)>(data => { if (data.channel.Equals(_journalHelper.GetJournalChannel(_persistenceId))) { Log.Debug("Message received"); switch (_state) { case State.Idle: // do nothing, no query is running and no client request was performed break; case State.Querying: _state = State.NotifiedWhenQuerying; break; case State.NotifiedWhenQuerying: // do nothing we already know that some new events may exist break; case State.WaitingForNotification: _state = State.Idle; Query(); break; } } else { Log.Debug($"Message from unexpected channel: {data.channel}"); } }); _subscription = _redis.GetSubscriber(); _subscription.Subscribe(_journalHelper.GetJournalChannel(_persistenceId), (channel, value) => { messageCallback.Invoke((channel, value)); }); } else { // start by first querying the current highest sequenceNr // for the given persistent id // stream will stop once this has been delivered _state = State.Initializing; var initCallback = GetAsyncCallback <long>(sn => { if (_toSequenceNr > sn) { // the initially requested max sequence number is higher than the current // one, restrict it to the current one _toSequenceNr = sn; } switch (_state) { case State.QueryWhenInitializing: // during initialization, downstream asked for an element, // let’s query elements _state = State.Idle; Query(); break; case State.Initializing: // no request from downstream, just go idle _state = State.Idle; break; default: Log.Error($"Unexpected source state when initializing: {_state}"); FailStage(new IllegalStateException($"Unexpected source state when initializing: {_state}")); break; } }); _redis.GetDatabase(_database).StringGetAsync(_journalHelper.GetHighestSequenceNrKey(_persistenceId)).ContinueWith(task => { if (!task.IsCanceled || task.IsFaulted) { if (task.Result.IsNull == true) { // not found, close CompleteStage(); } else { initCallback(long.Parse(task.Result)); } } else { Log.Error(task.Exception, "Error while initializing current events by persistent id"); FailStage(task.Exception); } }); } }
public override async Task <long> ReadHighestSequenceNrAsync(string persistenceId, long fromSequenceNr) { var highestSequenceNr = await Database.StringGetAsync(_journalHelper.GetHighestSequenceNrKey(persistenceId)); return(highestSequenceNr.IsNull ? 0L : (long)highestSequenceNr); }