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

   ## Summary
   
   Closes #5314 (one-shot, supersedes the earlier plan to split into multiple 
PRs).
   
   This PR delivers the cross-store fault-injection test suite for the unified 
state control
   plane introduced by #5301. The four individual Sub-PRs 
(#5310/#5311/#5312/#5313) each ship a
   contract test for their own store, but no test exists for **invariants that 
span two or more
   stores** — and those are exactly the invariants the runtime depends on. The 
6 scenarios here
   exercise the failure modes at the seams between stores, the runtime, and the 
cluster-shared Meta.
   
   ## What ships
   
   ### 4 test-harness primitives 
(`eventmesh-runtime/src/test/java/.../state/fault/`)
   - `MetaPartitionSwitch` — wraps a `MetaStore`, lets a test open/close a 
simulated network
     partition. Mutating ops throw `MetaPartitionException`; reads continue 
against the
     pre-partition snapshot.
   - `CrossStoreRaceProbe` — ordered log of cross-store operations 
(`DELIVERY_PUT/REMOVE`,
     `OFFSET_WRITE/READ`, `TASK_UPDATE`) with a monotonic `seq`, used to assert
     happens-before relationships.
   - `JvmCrashHarness` — spawns a child JVM, sentinel-file-driven 
`destroyForcibly()` (SIGKILL
     on POSIX, TerminateProcess on Windows), then relaunches against the same 
on-disk stores.
     Gated on `ENABLE_JVM_CRASH_HARNESS=true`; the in-process simulation in 
scenario 1 covers
     the same property without spawning a child JVM and runs everywhere.
   - `InMemorySubscriptionStore` — `ConcurrentHashMap`-backed 
`SubscriptionStore` for the split-
     brain scenario, so a test can hold two views of the world.
   
   ### 6 fault-injection scenarios (`CrossStoreFaultInjectionTest`, 596 LoC)
   
   | # | Scenario | Failure mode | Core invariant asserted |
   |---|----------|--------------|-------------------------|
   | 1 | `CrashMidAckReAck` | crash after offset-write, before MQ-ACK callback 
| recovery on a fresh dispatcher retires the delivery **without re-invoking the 
channel** (#5291 idempotency) |
   | 2 | `MetaPartitionDuringDlq` | Meta unreachable while dead-letter 
recording | `MetaBackedDeadLetterStore` throws `MetaPartitionException` — a 
loud failure, not a silent no-op — so the dispatcher keeps the delivery in 
flight and retries on heal (#5292) |
   | 3 | `A2aCancelMidStream` | cancel lands between PENDING and RUNNING | the 
`taskEpoch` guard rejects the late transition; the task converges on a single 
terminal state (#5302) |
   | 4 | `SubscriptionReRegisterAfterSplit` | subscription update during a Meta 
partition | after heal the latest write wins — nothing dropped, nothing 
duplicated (#5288, #5301 `SubscriptionStore`) |
   | 5 | `OffsetStoreRaceVsDeliveryStore` | cross-thread offset-advance vs 
retire race | the probe log proves **every `DELIVERY_REMOVE` is preceded by an 
`OFFSET_WRITE` at the same offset** (#5289 at-least-once) |
   | 6 | `A2aDispatchRaceVsTaskStore` | two dispatchers race on one task record 
| stale-epoch writes are rejected; concurrent `createTask` yields exactly one 
winner (#5291) |
   
   All 6 scenarios run fully in-process and deterministically. No 
Testcontainers / no Nacos /
   no Docker required.
   
   ### Documentation
   - `docs/eventmesh-uni-architecture-redesign.md`: new §13.2.12 "Cross-store 
fault-injection
     verification (#5314 Sub-PR D2)" — scope, the 4 harness primitives, the 
per-scenario
     assertion table, why Testcontainers is not used, and the two extension 
points.
     Numbered 13.2.12 because §13.2.11 was taken by the dual-topology matrix in 
PR #5317.
   
   ## Why one-shot, not split
   
   The 6 scenarios share the same 4 harness primitives. Splitting them would 
either (a)
   duplicate the harness across PRs, or (b) require a pre-PR that lands the 
harness first and
   has no assertions of its own. Keeping the harness slim (414 LoC across 4 
files) and bundling
   the scenarios keeps the review scope manageable while avoiding the
   pre-PR-without-assertions anti-pattern.
   
   ## Why no Testcontainers
   
   `#5314` originally listed 6 Testcontainers-based scenarios, but the 
in-process approach is
   strictly better for these failure modes:
   - MetaPartition and JvmCrash are deterministic fault-injection primitives. 
Wrapping them in
     a Nacos/Kafka container adds a network-latency surface that obscures the 
assertion rather
     than strengthening it.
   - CI sandboxes (Windows containers, macOS runners) often cannot run 
Docker-out-of-Docker, so
     the Testcontainers variant would be skipped in CI; the in-process variant 
runs everywhere.
   - 4 of 6 scenarios are algorithm-correctness properties about 
Meta/OffsetStore coordination
     (#5289, #5291, #5292); running them against real Nacos with network delays 
does not change
     the assertion outcome.
   
   ## How to verify locally
   
   ```bash
   # In-process: always runs
   ./gradlew :eventmesh-runtime:test --tests \
     org.apache.eventmesh.runtime.state.CrossStoreFaultInjectionTest
   
   # Cross-JVM (optional; requires process control / SIGKILL)
   ENABLE_JVM_CRASH_HARNESS=true ./gradlew :eventmesh-runtime:test --tests \
     org.apache.eventmesh.runtime.state.fault.JvmCrashHarness
   ```
   
   ## Relationship to existing suites (no overlap)
   
   - `SubscriptionStoreTest` / `SessionStoreTest` / `DeadLetterStoreTest` / 
`TaskStoreTest` /
     `DeliveryStateStoreTest` / `MetaBackedDeadLetterStoreTest` / 
`MetaBackedTaskStoreTest`
     cover **single-store contracts**.
   - `DeliveryRecoveryTest` covers **single-store recovery** 
(`dispatcher.recover()`).
   - `ClusterDeliveryFaultTest` covers **single-store faults** (partition 
ownership failure).
   - `CrossStoreFaultInjectionTest` (this PR) covers **cross-store 
collaborative faults**.
   
   ## Notes for reviewers
   
   - The 6 scenarios are additive: no production code is touched. The only 
main-source change
     in this branch is zero — the diff is 6 files, +1063 lines, 0 deletions.
   - Scenario 3/6 model the `taskEpoch` guard's documented contract (set at 
`createTask`, never
     reset): it rejects cross-restart staleness, not intra-JVM double-writes. 
Scenario 6's
     `concurrentDispatchersConvergeOnFreshestWrite` asserts the `createTask` 
`putIfAbsent`
     guarantee rather than inventing a second epoch-bump semantics.
   - `JvmCrashHarness` is shipped but inert unless 
`ENABLE_JVM_CRASH_HARNESS=true`, so it cannot
     make CI flaky.
   
   ## Checklist
   
   - [x] Rebased on the latest `apache/develop` (includes PR #5317)
   - [x] No production-code changes
   - [x] Architecture doc updated (§13.2.12)
   - [x] Apache CLA on file
   - [ ] CI green (Build / checkstyle / test)
   


-- 
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