private void SendBuffered() { foreach (var message in _buffer) { PersistentActor.Tell(message, ActorRefs.NoSender); } _buffer.Clear(); }
public void PersistentActorTest() { TestLauncherActor.Test(() => { var service = new MemoizePersistentService <string>(); var persistent = new PersistentActor <string>(service, "TestActor"); persistent.SendMessage(new EventSourceTest("A")); persistent.SendMessage(new EventSourceTest("B")); persistent.SendMessage(new EventSourceTest("C")); Assert.AreEqual("C", persistent.GetCurrent().Result()); var persistent2 = new PersistentActor <string>(service, "TestActor"); persistent2.Reload(); Assert.AreEqual("C", persistent2.GetCurrent().Result()); }); }
private void Fail(IllegalStateException cause) { _buffer.Clear(); PersistentActor.Tell(new ReplayMessagesFailure(cause), ActorRefs.NoSender); Context.Become(message => { if (message is ReplayedMessage) { // discard } else if (message is RecoverySuccess || message is ReplayMessagesFailure) { Context.Stop(Self); } else { return(false); } return(true); }); }
/// <summary> /// TBD /// </summary> /// <param name="message">TBD</param> /// <exception cref="ArgumentException"> /// This exception is thrown when the <see cref="Mode"/> is set to <see cref="ReplayFilterMode.Disabled"/>. /// </exception> /// <exception cref="IllegalStateException"> /// This exception is thrown when either the replayed event is in the wrong order or from an old writer. /// </exception> /// <returns>TBD</returns> protected override bool Receive(object message) { if (message is ReplayedMessage) { var r = (ReplayedMessage)message; if (DebugEnabled && _log.IsDebugEnabled) { _log.Debug($"Replay: {r.Persistent}"); } try { if (_buffer.Count == WindowSize) { var msg = _buffer.First; _buffer.RemoveFirst(); PersistentActor.Tell(msg.Value, ActorRefs.NoSender); } if (r.Persistent.WriterGuid.Equals(_writerUuid)) { // from same writer if (r.Persistent.SequenceNr < _sequenceNr) { var errMsg = $@"Invalid replayed event [sequenceNr={r.Persistent.SequenceNr}, writerUUID={r.Persistent.WriterGuid}] as the sequenceNr should be equal to or greater than already-processed event [sequenceNr={_sequenceNr}, writerUUID={_writerUuid}] from the same writer, for the same persistenceId [{r.Persistent.PersistenceId}]. Perhaps, events were journaled out of sequence, or duplicate PersistentId for different entities?"; LogIssue(errMsg); switch (Mode) { case ReplayFilterMode.RepairByDiscardOld: //discard break; case ReplayFilterMode.Fail: throw new IllegalStateException(errMsg); case ReplayFilterMode.Warn: _buffer.AddLast(r); break; case ReplayFilterMode.Disabled: throw new ArgumentException("Mode must not be Disabled"); } } else { // note that it is alright with == _sequenceNr, since such may be emitted by EventSeq _buffer.AddLast(r); _sequenceNr = r.Persistent.SequenceNr; } } else if (_oldWriters.Contains(r.Persistent.WriterGuid)) { // from old writer var errMsg = $@"Invalid replayed event [sequenceNr={r.Persistent.SequenceNr}, writerUUID={r.Persistent.WriterGuid}]. There was already a newer writer whose last replayed event was [sequenceNr={_sequenceNr}, writerUUID={_writerUuid}] for the same persistenceId [{r.Persistent.PersistenceId}]. Perhaps, the old writer kept journaling messages after the new writer created, or duplicate PersistentId for different entities?"; LogIssue(errMsg); switch (Mode) { case ReplayFilterMode.RepairByDiscardOld: //discard break; case ReplayFilterMode.Fail: throw new IllegalStateException(errMsg); case ReplayFilterMode.Warn: _buffer.AddLast(r); break; case ReplayFilterMode.Disabled: throw new ArgumentException("Mode must not be Disabled"); } } else { // from new writer if (!string.IsNullOrEmpty(_writerUuid)) { _oldWriters.AddLast(_writerUuid); } if (_oldWriters.Count > MaxOldWriters) { _oldWriters.RemoveFirst(); } _writerUuid = r.Persistent.WriterGuid; _sequenceNr = r.Persistent.SequenceNr; // clear the buffer from messages from other writers with higher SequenceNr var node = _buffer.First; while (node != null) { var next = node.Next; var msg = node.Value; if (msg.Persistent.SequenceNr >= _sequenceNr) { var errMsg = $@"Invalid replayed event [sequenceNr=${r.Persistent.SequenceNr}, writerUUID=${r.Persistent.WriterGuid}] from a new writer. An older writer already sent an event [sequenceNr=${msg.Persistent.SequenceNr}, writerUUID=${msg.Persistent.WriterGuid}] whose sequence number was equal or greater for the same persistenceId [${r.Persistent.PersistenceId}]. Perhaps, the new writer journaled the event out of sequence, or duplicate PersistentId for different entities?"; LogIssue(errMsg); switch (Mode) { case ReplayFilterMode.RepairByDiscardOld: _buffer.Remove(node); //discard break; case ReplayFilterMode.Fail: throw new IllegalStateException(errMsg); case ReplayFilterMode.Warn: // keep break; case ReplayFilterMode.Disabled: throw new ArgumentException("Mode must not be Disabled"); } } node = next; } _buffer.AddLast(r); } } catch (IllegalStateException ex) { if (Mode == ReplayFilterMode.Fail) { Fail(ex); } else { throw; } } } else if (message is RecoverySuccess || message is ReplayMessagesFailure) { if (DebugEnabled) { _log.Debug($"Replay completed: {message}"); } SendBuffered(); PersistentActor.Tell(message, ActorRefs.NoSender); Context.Stop(Self); } else { return(false); } return(true); }
/// <summary> /// TBD /// </summary> /// <param name="message">TBD</param> /// <exception cref="ArgumentException">TBD</exception> /// <exception cref="IllegalStateException">TBD</exception> /// <returns>TBD</returns> protected override bool Receive(object message) { if (message is ReplayedMessage) { var r = (ReplayedMessage)message; if (DebugEnabled && _log.IsDebugEnabled) { _log.Debug("Replay: " + r.Persistent); } try { if (_buffer.Count == WindowSize) { var msg = _buffer.First; _buffer.RemoveFirst(); PersistentActor.Tell(msg.Value, ActorRefs.NoSender); } if (r.Persistent.WriterGuid.Equals(_writerUuid)) { // from same writer if (r.Persistent.SequenceNr < _sequenceNr) { var errMsg = string.Format( "Invalid replayed event [{0}] in wrong order from " + "writer [{1}] with PersistenceId [{2}]", r.Persistent.SequenceNr, r.Persistent.WriterGuid, r.Persistent.PersistenceId); LogIssue(errMsg); switch (Mode) { case ReplayFilterMode.RepairByDiscardOld: //discard break; case ReplayFilterMode.Fail: throw new IllegalStateException(errMsg); case ReplayFilterMode.Warn: _buffer.AddLast(r); break; case ReplayFilterMode.Disabled: throw new ArgumentException("Mode must not be Disabled"); } } else { // note that it is alright with == _sequenceNr, since such may be emitted by EventSeq _buffer.AddLast(r); _sequenceNr = r.Persistent.SequenceNr; } } else if (_oldWriters.Contains(r.Persistent.WriterGuid)) { // from old writer var errMsg = string.Format( "Invalid replayed event [{0}] from old writer [{1}] with PersistenceId [{2}]", r.Persistent.SequenceNr, r.Persistent.WriterGuid, r.Persistent.PersistenceId); LogIssue(errMsg); switch (Mode) { case ReplayFilterMode.RepairByDiscardOld: //discard break; case ReplayFilterMode.Fail: throw new IllegalStateException(errMsg); case ReplayFilterMode.Warn: _buffer.AddLast(r); break; case ReplayFilterMode.Disabled: throw new ArgumentException("Mode must not be Disabled"); } } else { // from new writer if (!string.IsNullOrEmpty(_writerUuid)) { _oldWriters.AddLast(_writerUuid); } if (_oldWriters.Count > MaxOldWriters) { _oldWriters.RemoveFirst(); } _writerUuid = r.Persistent.WriterGuid; _sequenceNr = r.Persistent.SequenceNr; // clear the buffer from messages from other writers with higher SequenceNr var node = _buffer.First; while (node != null) { var next = node.Next; var msg = node.Value; if (msg.Persistent.SequenceNr >= _sequenceNr) { var errMsg = string.Format( "Invalid replayed event [{0}] in buffer from old writer [{1}] with PersistenceId [{2}]", r.Persistent.SequenceNr, r.Persistent.WriterGuid, r.Persistent.PersistenceId); LogIssue(errMsg); switch (Mode) { case ReplayFilterMode.RepairByDiscardOld: _buffer.Remove(node); //discard break; case ReplayFilterMode.Fail: throw new IllegalStateException(errMsg); case ReplayFilterMode.Warn: // keep break; case ReplayFilterMode.Disabled: throw new ArgumentException("Mode must not be Disabled"); } } node = next; } _buffer.AddLast(r); } } catch (IllegalStateException ex) { if (Mode == ReplayFilterMode.Fail) { Fail(ex); } else { throw; } } } else if (message is RecoverySuccess || message is ReplayMessagesFailure) { SendBuffered(); PersistentActor.Tell(message, ActorRefs.NoSender); Context.Stop(Self); } else { return(false); } return(true); }