Class RedisOutboxEntry

java.lang.Object
com.aim2be.platform.outbox.redis.RedisOutboxEntry

public final class RedisOutboxEntry extends Object
JSON-serialisable value stored in the :entries Hash field for one outbox event (design doc core storage model). One field per event_id; the value is this object encoded as JSON.

The Avro payload bytes are carried Base64-encoded because JSON is not binary-safe — the relay/poller decode them back to byte[] before the Kafka send. PII is never inlined: per ADR-0002 §3b the events are pointer-based, so the payload is an Avro envelope referencing aggregate ids, not raw personal data.

Field semantics mirror OutboxRecord so the two backends present a uniform lifecycle:

  • statusPENDING / SENT / FAILED (the ARCHIVED PG state has no Redis analogue; retention HDELs SENT entries instead, refinement #9).
  • retries — incremented on each failed attempt.
  • createdAt — enqueue epoch-ms (informational; the :pending score is nextAttemptAt, NOT createdAt, refinement #5).
  • sentAt — set on the PENDING→SENT transition; drives retention eligibility.
  • lastError — truncated failure message (refinement parity with OutboxRecord.LAST_ERROR_MAX_LENGTH).

Instances are immutable; lifecycle transitions are performed in Lua on the server side (the JSON is re-read + re-written inside the EVAL), so this class only needs round-trip (de)serialisation + conversion to/from OutboxRecord for the SPI's findPendingBatch contract.

@JsonInclude(NON_NULL) is load-bearing, not cosmetic. The Lua transition scripts (outbox-mark-sent.lua, retention-delete-sent.lua) read the entry back via cjson.decode and test if not entry.sentAtEpochMs. Lua's cjson decodes a JSON null to the truthy sentinel cjson.null (NOT Lua nil), so a serialised "sentAtEpochMs":null would make not entry.sentAtEpochMs false and break the age check. Omitting null fields entirely keeps the Lua nil-test correct.

  • Constructor Details

    • RedisOutboxEntry

      public RedisOutboxEntry(UUID aggregateId, UUID eventId, String topic, int schemaVersion, String payloadBase64, String status, int retries, long createdAtEpochMs, Long sentAtEpochMs, String lastError)
      Full constructor used by Jackson on deserialisation and by the relay/SPI on construction.
      Parameters:
      aggregateId - aggregate identifier (Kafka partition-key hint)
      eventId - outbox event id (Hash field key)
      topic - Kafka topic
      schemaVersion - Avro schema version mirrored for operator triage
      payloadBase64 - Base64-encoded Avro payload bytes
      status - lifecycle status string (PENDING / SENT / FAILED)
      retries - publish-attempt counter
      createdAtEpochMs - enqueue time, epoch-ms
      sentAtEpochMs - publish-success time, epoch-ms, or null when not yet sent
      lastError - truncated failure message, or null
  • Method Details

    • pendingFrom

      public static RedisOutboxEntry pendingFrom(OutboxRecord record)
      Builds a fresh PENDING entry from an OutboxRecord the orchestration layer populated.
      Parameters:
      record - the PENDING record (aggregateId, eventId, topic, schemaVersion, payloadBytes, createdAt populated); never null
      Returns:
      a new PENDING entry with retries=0, sentAt=null, lastError=null
    • toRecord

      public OutboxRecord toRecord()
      Reconstructs an OutboxRecord view of this entry for the OutboxBackend.findPendingBatch(int) contract. The recordVersion is left at its default (0) — the Redis backend uses status-guarded Lua transitions, not JPA optimistic locking, so the version field is inert here.
      Returns:
      a transient OutboxRecord mirroring this entry's fields
    • decodePayload

      public byte[] decodePayload()
      Returns:
      the Avro payload bytes, Base64-decoded from payloadBase64
    • getAggregateId

      public UUID getAggregateId()
    • getEventId

      public UUID getEventId()
    • getTopic

      public String getTopic()
    • getSchemaVersion

      public int getSchemaVersion()
    • getPayloadBase64

      public String getPayloadBase64()
    • getStatus

      public String getStatus()
    • getRetries

      public int getRetries()
    • getCreatedAtEpochMs

      public long getCreatedAtEpochMs()
    • getSentAtEpochMs

      public Long getSentAtEpochMs()
    • getLastError

      public String getLastError()
    • toString

      public String toString()
      Overrides:
      toString in class Object