Class RedisOutboxRelay
Why this exists separately from OutboxPublisher.
The PG publisher registers its hot relay on a Spring
TransactionSynchronization.afterCommit() hook — UNCONDITIONALLY, per
the OutboxBackend.requiresActiveTransaction() @apiNote. A
transaction-free Redis backend invoked outside a Spring transaction would
fail at that relay-registration step with
IllegalStateException("Transaction synchronization is not active").
Phase 0b therefore owns its OWN relay: it does NOT route through the PG
OutboxPublisher. After the consumer enqueues an entry via
RedisOutboxBackend.persistPending(com.aim2be.platform.outbox.OutboxRecord), it calls relay(OutboxRecord)
(or relayAsync(OutboxRecord)) to attempt the Kafka send immediately;
on failure the RedisOutboxPollerWorker cold path drains it later.
The send is gated by a Resilience4j CircuitBreaker
(CIRCUIT_BREAKER_NAME) mirroring the PG publisher's
im2be-outbox-relay breaker, so a degraded broker short-circuits the
hot path while the cold poller keeps draining PENDING entries.
Delivery is at-least-once (ADR-0014 D-9): a successful Kafka ack before the
markSent write that then fails leaves a PENDING entry whose message is
already delivered — the next poller tick re-sends it and the consumer-side
dedup (processed-kafka-events) absorbs the duplicate.
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringResilience4j breaker instance name for the Redis hot relay. -
Constructor Summary
ConstructorsConstructorDescriptionRedisOutboxRelay(OutboxBackend backend, org.springframework.kafka.core.KafkaTemplate<byte[], byte[]> kafkaTemplate, io.github.resilience4j.circuitbreaker.CircuitBreaker circuitBreaker, RedisOutboxMetrics metrics, int maxRetries, long sendTimeoutMs) -
Method Summary
Modifier and TypeMethodDescriptionbooleanrelay(OutboxRecord record) Hot-relays one entry SYNCHRONOUSLY: blocks on the Kafka ack (bounded by the caller's producer config), then applies themarkSent/markFailureAttempttransition.voidrelayAsync(OutboxRecord record) Hot-relays one entry FIRE-AND-FORGET: enqueues the send and reconciles the outcome on the Kafka client thread'swhenCompletecallback.
-
Field Details
-
CIRCUIT_BREAKER_NAME
Resilience4j breaker instance name for the Redis hot relay. Distinct from the PG publisher'sim2be-outbox-relayso the two backends' breaker windows never alias when both are (hypothetically) on one classpath.- See Also:
-
-
Constructor Details
-
RedisOutboxRelay
public RedisOutboxRelay(OutboxBackend backend, org.springframework.kafka.core.KafkaTemplate<byte[], byte[]> kafkaTemplate, io.github.resilience4j.circuitbreaker.CircuitBreaker circuitBreaker, RedisOutboxMetrics metrics, int maxRetries, long sendTimeoutMs) - Parameters:
backend- the SPI (the Redis backend) for the markSent / markFailureAttempt transitions; nevernullkafkaTemplate- byte-key/byte-value Kafka template; nevernullcircuitBreaker- the hot-relay breaker; nevernullmetrics- the metrics binder; nevernullmaxRetries- retry budget passed toOutboxBackend.markFailureAttempt(java.util.UUID, java.util.UUID, java.lang.String, int)so the hot path uses the same budget as the cold pollersendTimeoutMs- bounded wait on the synchronousrelay(com.aim2be.platform.outbox.OutboxRecord)Kafka ack (same budget the cold poller'stryColdPublishuses); a stalled broker MUST NOT block the calling thread indefinitely
-
-
Method Details
-
relay
Hot-relays one entry SYNCHRONOUSLY: blocks on the Kafka ack (bounded by the caller's producer config), then applies themarkSent/markFailureAttempttransition. Returnstrueon a confirmed SENT transition,falseotherwise (breaker open, send failed, or the markSent applied 0 rows because the poller already SENT it).Synchronous form is the simplest to reason about for the inline path and is what the ITs exercise;
relayAsync(OutboxRecord)is the fire-and-forget variant for latency-sensitive callers.- Parameters:
record- the PENDING entry to relay; nevernull- Returns:
trueon confirmed SENT, elsefalse
-
relayAsync
Hot-relays one entry FIRE-AND-FORGET: enqueues the send and reconciles the outcome on the Kafka client thread'swhenCompletecallback. Returns immediately; the caller does not block on the broker. Used by latency-sensitive callers (e.g. the per-connect ticket mint) where blocking the request thread on the broker is unacceptable.- Parameters:
record- the PENDING entry to relay; nevernull
-