Replay
A reactor sees the same event twice for different reasons: once as it happens, and again whenever its observer is replayed. Those often call for different work — a confirmation that should go out once when an order is placed has no business going out again while a read model is being rebuilt.
Mark a second handler for the same event type with the Replay attribute and it takes over for the duration
of the replay.
using Cratis.Chronicle.Events;using Cratis.Chronicle.Reactors;
[EventType]public record ReplayAwareOrderPlaced(string OrderId);
public class ReplayAwareOrderReactor : IReactor{ public void SendConfirmation(ReplayAwareOrderPlaced @event) { // Runs as the event happens. }
[Replay] public void RebuildProjectionCache(ReplayAwareOrderPlaced @event) { // Runs instead of SendConfirmation while the observer is replaying. }}import io.cratis.chronicle.events.EventTypeimport io.cratis.chronicle.observation.Reactorimport io.cratis.chronicle.observation.Replay
@EventTypedata class ReplayAwareOrderPlaced(val orderId: String)
@Reactorclass ReplayAwareOrderReactor { fun sendConfirmation(event: ReplayAwareOrderPlaced) { // Runs as the event happens. }
@Replay fun rebuildProjectionCache(event: ReplayAwareOrderPlaced) { // Runs instead of sendConfirmation while the observer is replaying. }}import io.cratis.chronicle.events.EventType;import io.cratis.chronicle.observation.Reactor;import io.cratis.chronicle.observation.Replay;
@EventTyperecord ReplayAwareOrderPlaced(String orderId) {}
@Reactorclass ReplayAwareOrderReactor { void sendConfirmation(ReplayAwareOrderPlaced event) { // Runs as the event happens. }
@Replay void rebuildProjectionCache(ReplayAwareOrderPlaced event) { // Runs instead of sendConfirmation while the observer is replaying. }}Elixir does not support this workflow yet.import { eventType, reactor, replay } from '@cratis/chronicle';import { field } from '@cratis/fundamentals';
@eventType()class ReplayAwareOrderPlaced { @field(String) readonly orderId: string;
constructor(orderId: string = '') { this.orderId = orderId; }}
@reactor()class ReplayAwareOrderReactor { replayAwareOrderPlaced(event: ReplayAwareOrderPlaced): void { // Runs as the event happens. }
@replay() replayReplayAwareOrderPlaced(event: ReplayAwareOrderPlaced): void { // Runs instead of replayAwareOrderPlaced during replay. }}The rules are:
- With a
Replayhandler, only it runs during a replay — the regular handler does not also run. - Without one, the regular handler runs during a replay exactly as it always has, so adding this to one event type changes nothing for the others.
- An event type handled only by a
Replayhandler is still subscribed to, so the replay it exists for delivers it.
To be told when a replay starts and ends, rather than to change what a handler does during it, see Replay notifications.
Reach for OnceOnly instead when the side effect should simply not happen again. Use Replay when
a replay needs to do something different rather than nothing. Neither covers the re-delivery that follows a
failed partition being recovered — for that, see Delivery identity.