qqeasonchen opened a new pull request, #5311:
URL: https://github.com/apache/eventmesh/pull/5311

   ## Sub-PR B: DeliveryStateStore (issue #5301 §DeliveryStateStore)
   
   Makes in-flight delivery state survive a hard JVM restart. Every delivery is 
now persisted to a `DeliveryStateStore` on deliver/ack/nack/tick, and on 
startup the dispatcher's `recover()` re-ACKs each persisted record against the 
`OffsetStore` so no client is left without a progress advance and no delivery 
becomes an orphan.
   
   ### What's in this PR
   
   * **`DeliveryStateStore`** — new interface in 
`org.apache.eventmesh.runtime.state`:
     - `put(Record) / remove(String) / get(String) / iterate(Consumer<Record>) 
/ count() / flush() / close()`
     - `Record` carries `(deliveryId, topic, partition, offset, clientId, 
attempt, nextAttemptAtMs, encodedEvent)` — channel and mqAckCallback are 
runtime references that do NOT survive restart
     - `Record.toDelivery()` rebuilds a live `Delivery` for tick / iterate use
   
   * **`InMemoryDeliveryStateStore`** — test contract baseline 
(ConcurrentHashMap-backed)
   
   * **`RocksDBDeliveryStateStore`** — production; key=deliveryId, value=ASCII 
line with base64-wrapped event bytes. Mirrors the `RocksDBOffsetStore` pattern 
(no extra dependency for the same field count).
   
   * **`ReliableDispatcher`** now depends on a `DeliveryStateStore` 
(constructor parameter, default `InMemory`). The previous in-memory 
`ConcurrentHashMap` is gone:
     - `tick()` iterates the store
     - `ack()` / `nack()` remove / put via the store
     - `deliver()` persists via the store
     - Retry-vs-ack race is guarded at the iterate-then-act layer (snapshot 
semantics + re-read)
   
   * **`recover()`** — new method: on startup, walk the store, write the stored 
offset as if the client had ACKed, advance the MQ physical cursor 
(`emmqoffset`/`emmqpartition`), remove the record. **Never re-runs the 
channel** — the broker has already either redelivered (Kafka / RocketMQ 4.x 
PULL) or considered the message gone (RocketMQ 5.x POP). Idempotent.
   
   * **`UniIngressService`** wires `dispatcher.recover()` into its constructor 
so a fresh JVM picks up unacked deliveries from a prior process without an 
explicit boot step.
   
   * **`package-info`** updated: `DeliveryStateStore` is now a real Javadoc 
link, not a placeholder.
   
   ### Tests
   
   * **`DeliveryStateStoreTest`** — in-memory + RocksDB contract harness 
(put/remove/get/count/iterate, including `rocksDbPersistsAcrossClose`)
   
   * **`DeliveryRecoveryTest`** — fault injection: crash mid-delivery, fresh 
dispatcher on the same store, `recover()` retires all in-flight records and 
advances the `OffsetStore` without re-running the channel; idempotency; 
empty-store no-op; `nextAttemptAtMs` round-trip
   
   ### Closes
   
   * **#5294** (unacknowledged broadcast deliveries lost during cursor recovery)
   * **#5295** (RocketMQ 5 POP broker ACK not gated on distribution completion)
   
   ### Acceptance criteria touched
   
   * `DeliveryStateStore` recovers across a hard restart — no orphaned 
in-flight deliveries ✓
   * ACK validation binds to delivery ownership — `deliveryId` is now persisted 
via `DeliveryStateStore`, so a stale ACK cannot match a fresh delivery after 
restart ✓
   
   ### Sub-PR ordering
   
   A (PR #5310 ✓ merged) → **B (this PR)** → C (DeadLetterStore + TaskStore 
Meta backends) → D (cross-store fault-injection)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to