Class Replay
A deliberate replay off the dead-letter queue (Redrive) re-runs the handler, and
the handler's external side-effects re-fire: a second charge, a duplicate email.
Idempotent.wrap stops an accidental duplicate; it does not stop the
intended reprocess from re-firing effects that already happened. This closes that gap.
The marker that says "this is a replay, skip the external effects" rides out of band
as the HEADER_REPLAY_BYPASS transport header — never in the frozen envelope (GR-1).
Redrive.redrive(com.babelqueue.Redrive.Transport, java.lang.String, com.babelqueue.Redrive.Options) stamps it when its options set bypass and the transport is a
Redrive.HeaderPublisher; a consume adapter, having reserved the message with its
headers, surfaces the flag for the duration of the handler via process(java.util.Map<java.lang.String, java.lang.String>, com.babelqueue.Replay.Effect):
Redrive.Reserved msg = transport.pop(queue);
Envelope env = EnvelopeCodec.decode(msg.body());
Replay.process(msg.headers(), () -> handler.handle(env)); // handler can now query isReplay()
A handler wraps its external, non-idempotent side in bypassExternalEffects(com.babelqueue.Replay.Effect) so a
replay re-runs the idempotent core but skips effects that already fired. The Java core is
codec-only, so this is the core/runtime API + in-memory testing; a concrete broker transport
carries the header once it implements Redrive.HeaderPublisher (a follow-up).
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic interfaceAn action that may throw — the external side of a handler. -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringThe out-of-band transport headerRedrivestamps (withbypass) on a replayed message, and that a consume adapter surfaces to the handler viaprocess(java.util.Map<java.lang.String, java.lang.String>, com.babelqueue.Replay.Effect). -
Method Summary
Modifier and TypeMethodDescriptionstatic voidbypassExternalEffects(Replay.Effect effect) Runseffectunless the current message is a replay, in which case it is skipped.static booleanisReplay()Reports whether the message currently being handled was redriven with the replay-bypass marker — i.e.static voidprocess(Map<String, String> headers, Replay.Effect body) Runsbodywith the replay flag derived from a reserved message's transport headers (the presence ofHEADER_REPLAY_BYPASS), restoring the prior flag afterwards.
-
Field Details
-
HEADER_REPLAY_BYPASS
The out-of-band transport headerRedrivestamps (withbypass) on a replayed message, and that a consume adapter surfaces to the handler viaprocess(java.util.Map<java.lang.String, java.lang.String>, com.babelqueue.Replay.Effect).- See Also:
-
-
Method Details
-
isReplay
public static boolean isReplay()Reports whether the message currently being handled was redriven with the replay-bypass marker — i.e. a deliberate replay whose external side-effects should be skipped. Meaningful only inside aprocess(java.util.Map<java.lang.String, java.lang.String>, com.babelqueue.Replay.Effect)scope the consumer established from the message's headers.- Returns:
- whether the current handling is a bypassed replay
-
process
Runsbodywith the replay flag derived from a reserved message's transport headers (the presence ofHEADER_REPLAY_BYPASS), restoring the prior flag afterwards. A consume adapter wraps each handler invocation in this.- Parameters:
headers- the reserved message's out-of-band headers (may be null/empty)body- the handler invocation to run within the replay scope- Throws:
Exception- whateverbodythrows
-
bypassExternalEffects
Runseffectunless the current message is a replay, in which case it is skipped. Wrap the external, non-idempotent side of a handler — sending an email, charging a card, calling a third party — so a replay re-runs the idempotent core but does not re-fire effects that already happened.- Parameters:
effect- the external side-effect to run only when this is not a replay- Throws:
Exception- whatevereffectthrows
-