Tracking sub-issue of #5354. Part of the production-HA acceptance plan (Phase 2, P1).
What this PR adds
A Testcontainers-based E2E harness that exercises the cross-instance, multi-tenant, fencing-aware production code path. The test class is the answer to #5347 (D2b) for the production-HA flow specifically; the A2A-flow Testcontainers harness remains a separate issue.
Harness layout
A new eventmesh-testcontainers module (build.gradle + gradle.properties, Apache 2.0 license) provides:
MetaStoreContainer: Nacos 2.3.x with persistent volume; expose 8848.
BrokerContainer: RocketMQ 5.3.x nameserver + broker; expose 9876 (NS) and 10911 (broker).
EventMeshContainer: built from docker/Dockerfile with eventmesh.delivery.topology=PARTITION_OWNED_PULL, eventmesh.meta.store=nacos, Nacos address injected; expose 8081 (admin) and 10000 (HTTP publish).
Test cases
CrashRestartTest: 2 Runtime instances, 1 topic with 4 partitions, 1000 events published; kill instance A, restart, assert all 1000 events delivered exactly once across the surviving + restarted instances.
TakeoverTest: 2 instances, instance A owns all 4 partitions, publish 100 events with no ACK from subscribers; kill A; instance B acquires the partitions, redelivers the 100 in-flight events (within the at-least-once bound), and continues.
StaleOwnerDispatchRejectedTest: 2 instances, instance A dispatches, manually bump the partition token in Nacos to simulate instance C taking over; A's next dispatch throws StaleOwnerException; the dispatcher removes the partition from its owned set on the next tick.
QuotaA2AStreamCancelTest: 1 instance, open 5 A2A streams (CONNECTIONS quota=3); 2 must be rejected with 429; cancel 1, the next submit must succeed.
MixedBackendTopologyTest: 1 instance with PARTITION_OWNED_PULL against RocketMQ 5 lite topic (partitionCountKnown=false) + no degradeWithoutPartitionCount flag; the boot fails with the documented error message; set the flag, the instance boots and falls back to the documented behavior.
Acceptance criteria
- The
eventmesh-testcontainers module compiles and its gradle :eventmesh-testcontainers:test runs in CI within 15 minutes.
- The 5 test cases above are green.
docs/production-readiness.md is updated to point to the harness and the supported backend × topology matrix.
- A row in
docs/architecture-review/evidence.md links each test to its commit + CI run.
Verification
./gradlew :eventmesh-testcontainers:test
Depends on / blocks
References
eventmesh-testcontainers/ (new module)
eventmesh-runtime/.../cluster/PartitionOwnership.java
eventmesh-runtime/.../delivery/ReliableDispatcher.java
eventmesh-runtime/.../security/gate/SecurityGate.java
eventmesh-runtime/.../a2a/A2AGatewayHttpHandler.java
docs/production-readiness.md section 5
Part of the production-HA topology in #5354. See also #5352 and #5353. Closes #5347 for the production-HA flow (D2a #5346 was the A2A flow; D2b is split).
Tracking sub-issue of #5354. Part of the production-HA acceptance plan (Phase 2, P1).
What this PR adds
A Testcontainers-based E2E harness that exercises the cross-instance, multi-tenant, fencing-aware production code path. The test class is the answer to #5347 (D2b) for the production-HA flow specifically; the A2A-flow Testcontainers harness remains a separate issue.
Harness layout
A new
eventmesh-testcontainersmodule (build.gradle + gradle.properties, Apache 2.0 license) provides:MetaStoreContainer: Nacos 2.3.x with persistent volume; expose 8848.BrokerContainer: RocketMQ 5.3.x nameserver + broker; expose 9876 (NS) and 10911 (broker).EventMeshContainer: built fromdocker/Dockerfilewitheventmesh.delivery.topology=PARTITION_OWNED_PULL,eventmesh.meta.store=nacos, Nacos address injected; expose 8081 (admin) and 10000 (HTTP publish).Test cases
CrashRestartTest: 2 Runtime instances, 1 topic with 4 partitions, 1000 events published; kill instance A, restart, assert all 1000 events delivered exactly once across the surviving + restarted instances.TakeoverTest: 2 instances, instance A owns all 4 partitions, publish 100 events with no ACK from subscribers; kill A; instance B acquires the partitions, redelivers the 100 in-flight events (within theat-least-oncebound), and continues.StaleOwnerDispatchRejectedTest: 2 instances, instance A dispatches, manually bump the partition token in Nacos to simulate instance C taking over; A's next dispatch throwsStaleOwnerException; the dispatcher removes the partition from its owned set on the next tick.QuotaA2AStreamCancelTest: 1 instance, open 5 A2A streams (CONNECTIONS quota=3); 2 must be rejected with 429; cancel 1, the next submit must succeed.MixedBackendTopologyTest: 1 instance withPARTITION_OWNED_PULLagainst RocketMQ 5 lite topic (partitionCountKnown=false) + nodegradeWithoutPartitionCountflag; the boot fails with the documented error message; set the flag, the instance boots and falls back to the documented behavior.Acceptance criteria
eventmesh-testcontainersmodule compiles and itsgradle :eventmesh-testcontainers:testruns in CI within 15 minutes.docs/production-readiness.mdis updated to point to the harness and the supported backend × topology matrix.docs/architecture-review/evidence.mdlinks each test to its commit + CI run.Verification
Depends on / blocks
References
eventmesh-testcontainers/(new module)eventmesh-runtime/.../cluster/PartitionOwnership.javaeventmesh-runtime/.../delivery/ReliableDispatcher.javaeventmesh-runtime/.../security/gate/SecurityGate.javaeventmesh-runtime/.../a2a/A2AGatewayHttpHandler.javadocs/production-readiness.mdsection 5Part of the production-HA topology in #5354. See also #5352 and #5353. Closes #5347 for the production-HA flow (D2a #5346 was the A2A flow; D2b is split).