Skip to content

[Architecture Review][P1] #5301 Sub-PR D: cross-store fault-injection E2E #5314

Description

@qqeasonchen

Phase 2 follow-up — Cross-store fault-injection E2E (#5301 Sub-PR D)

Source: Issue #5301 — Sub-PR D, deferred at issue close.
Severity: P1 (reliability correctness; not on the critical path for A2A / Runtime merge)
Blocks: — none directly; motivates future changes to DeadLetterStore / TaskStore / DeliveryStateStore

Why this is split out

The unified state control-plane stores (OffsetStore / SubscriptionStore / DeliveryStateStore / SessionStore / DeadLetterStore / TaskStore) were landed as separate SPI + concrete backends across PRs #5310 (Sub-PR A), #5311 (B), #5312 (C). Each sub-PR carried its own unit tests, but the six stores have never been exercised together in a fault-injection harness. Sub-PR D was the original plan for that.

Scenarios to cover

All six scenarios target the develop branch as of apache/eventmesh#5313 merged.

  1. Crash mid-ACK → re-ACK on restart

    • Setup: submit a message; pause the dispatch loop just after DeliveryStateStore.put but before the connector ACK returns.
    • Inject: hard kill the JVM.
    • Expectation on restart: ReliableDispatcher.recover() re-enqueues the same deliveryId; consumer sees exactly one delivery.
  2. Meta partition during DLQ transition

    • Setup: force a DLQ write; pause the connector ack callback.
    • Inject: kill the Meta client (close socket / 30s pause).
    • Expectation: DeadLetterStore.recordDeadLetter is the durable gate; the source delivery is NOT retired until the Meta write is acknowledged. After Meta recovers, the delivery is retired.
  3. A2A task cancelled mid-stream

    • Setup: start a long-running A2A TaskRecord (e.g. via A2AGatewayService.submitTask against a slow agent).
    • Inject: cancelTask(taskId) while the agent is mid-SSE.
    • Expectation: TaskStore.updateStatus(..., Status.CANCELED, ...) succeeds; pending SSE subscribers get a terminal CANCELED event; the agent receives a cancel signal.
  4. Subscription re-register after split

    • Setup: a consumer group is registered via SubscriptionStore.
    • Inject: Meta cluster split for 30s; two Runtime instances see different Meta views.
    • Expectation: when the split heals, both instances converge to the same subscription set; no duplicate redelivery for already-ACKed messages.
  5. Offset-store race vs. delivery-store recovery

    • Setup: offset store and delivery state store are co-located but updated independently.
    • Inject: after a crash, recover delivery state first, then offset state.
    • Expectation: ACKs that landed between the two writes are honored exactly once.
  6. A2A dispatch race vs. TaskStore state

    • Setup: an A2A agent is registered; a task is submitted through the Runtime dispatcher.
    • Inject: invoke cancelTask exactly at the moment the dispatcher dispatches the task to the agent.
    • Expectation: outcome is one of (delivered then canceled) or (canceled before dispatch) — both observable; final TaskRecord.status is CANCELED; DeadLetterStore does NOT receive a record for the canceled task.

Test harness

  • Use Testcontainers + JUnit 5 (Java 21) — pattern established by the Runtime module's existing fault-injection tests in eventmesh-runtime/src/test/java/.../cluster/ClusterDeliveryFaultTest.
  • For Meta: spin up the in-process Nacos-compatible Meta (InProcessMetaService) already used by the unit tests, plus an optional Meta kill-switch (close + reopen the socket) for partition scenarios.
  • For the JVM crash scenario: use a child-process harness (ProcessBuilder + Runtime.exec) so the test can SIGKILL the runtime under test and re-launch it.

Acceptance

  • All six scenarios have a JUnit test that reliably reproduces the failure mode (5/5 runs in CI).
  • A short doc note (docs/architecture/state-stores-fault-injection.md) lists the scenarios and the property each one asserts.

Out of scope

  • Performance / load (separate issue if needed).
  • Production fault-injection tooling.
  • Changes to the store APIs themselves — this is purely verification.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions