test(e2e): event-driven harness with per-controller parking and deterministic cancel test#349
Draft
sbalabanov wants to merge 2 commits into
Draft
test(e2e): event-driven harness with per-controller parking and deterministic cancel test#349sbalabanov wants to merge 2 commits into
sbalabanov wants to merge 2 commits into
Conversation
…y merge queue The orchestrator registers its internal merge pipeline stage and the runway-owned merge queue in one topic registry over one shared queue backend, and both used the topic key "merge" (topickey.TopicKeyMerge == runwaymq.TopicKeyMerge). The publish-only runway entry silently shadowed the internal stage entry (last write wins), collapsing the two topics into one: - runway consumed the internal batch-ID messages as zero-step MergeRequests and reported SUCCEEDED without ever seeing the real steps; - the orchestrator merge controller's real MergeRequest publish was then silently dropped by the queue's (topic, partition_key, id) dedupe, because it reuses the batch ID already published to the same topic. The pipeline still reached "landed" only because the zero-step result correlates by batch ID — with a real merger nothing would ever have been merged. Found by the e2e harness asserting runway's merge signal carries the request's step IDs. Rename the internal stage key and wire name to "mergebatch", and make consumer.NewTopicRegistry reject duplicate topic keys so a future collision fails at startup instead of silently misrouting messages. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
…ministic cancel test Rework the SubmitQueue e2e suite so tests block on signals the pipeline pushes to them instead of polling with hard-coded timeouts, add a per-controller parking primitive so any pipeline stage can be starved deterministically, and add a cancellation test that deterministically reaches the terminal "cancelled" state. - Queue observer (observer_test.go): test-owned consumer groups subscribe to the log topic and runway's check/signal topics over the published mysql-queue port. Delivery state is keyed per consumer group, so the observer receives a copy of every message without stealing from the services and without any service changes. Tests await pushed events (status transitions, runway's answers on the wire). - No hard-coded timeouts: every wait is bounded by a suite context whose deadline derives from the test binary deadline (Bazel test timeout) minus a teardown margin. The old persistTimeout constant is gone; the remaining polls (Status RPC and request_log, both eventually consistent behind the gateway log consumer) run at a fixed cadence under that context. - StageHold (testutil/stagehold.go): parks any single queue consumer controller with zero service changes by planting a phantom partition lease (far-future lease_renewed_at) in the MySQL queue's partition-leases table for (consumer_group, topic, partition_key). The plain INSERT fails loudly if the partition is already leased, so holds must be planted before the stage first consumes (pre-hold contract). Release() deletes the row (idempotent after success, retryable on failure) and consumption resumes on the service's next discovery tick. Held messages are never delivered, so parking has no retry-budget or DLQ interaction. - Stage catalog (stage_test.go): enumerates every queue consumer controller across the three services — gateway (1), orchestrator (12 primary + 12 DLQ), and runway (2) — with a holdStage() suite helper, so tests can starve any controller x partition. - Lease-semantics contract test (integration, messagequeue/mysql): pins the guarantees StageHold relies on — a held partition starves the group's consumer while other partitions keep flowing, releasing the hold resumes delivery, and Release is idempotent. - Deterministic cancel test: park the request at the merge-conflict-check boundary with a StageHold on runway's merge-conflict-check consumer (partition = queue name) instead of stopping/starting the runway container (confirmed by a pushed event), cancel, await the pushed "cancelled" event, then assert the terminal store state, that no batch ever enrolled the request, and that runway's late signal after the hold is released does not resurrect it. The connectRunway re-dial helper is gone: services start once in SetupSuite and stop once in TearDownSuite. - Runway is now first-class in the suite: gRPC ping test, graceful shutdown exit-code check, and happy-path assertions that runway answered both the merge-conflict check and the merge with SUCCEEDED (the merge signal must carry the request's step IDs — the assertion that exposed the merge topic collision fixed in the parent commit). - testutil: ComposeStack drops StartService — there is no on-demand service start/stop mid-suite anymore (StopService is retained for the graceful-shutdown verification in teardown); compose command prefers docker compose V2 (the harness relies on V2-only "up --wait"); the stack uses a background context so teardown outlives the suite wait budget; TestLogger is safe for concurrent use. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bdc9c84 to
09e73e3
Compare
94e242a to
087124c
Compare
Collaborator
|
This PR could not be automatically rebased after its base PR was merged. The rebase hit conflicts that need manual resolution. To fix manually: git fetch origin
git checkout e2e-event-harness
git rebase --onto origin/main 087124c1ac6613d95786c55c7b12dad0e3509203 e2e-event-harness
# resolve conflicts, then:
git push --force-with-leaseThen update this PR's base branch: gh pr edit 349 --base main |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacks on #348 (base branch
fix-merge-topic-collision).What this is
Reworks the SubmitQueue e2e suite into a push-based event plane: test-owned consumer groups subscribe to the log topic and runway's check/signal topics over the published mysql-queue port. Delivery state in the MySQL queue is keyed per consumer group, so the observer receives a copy of every message without stealing from the services and with zero service changes. Tests await pushed events (status transitions, runway's answers on the wire) instead of polling.
There are no hard-coded timeouts: every wait is bounded by a suite context whose deadline derives from
t.Deadline()(the Bazel test timeout) minus a teardown margin. The remaining polls (Status RPC and request_log, both eventually consistent behind the gateway log consumer) run at a fixed cadence under that context.NEW: per-controller parking with StageHold
testutil.StageHoldparks any single queue consumer controller by planting a phantom partition lease — a row with a far-futurelease_renewed_at— in the MySQL queue's partition-leases table for (consumer_group, topic, partition_key). The queue's expiry-based steal semantics (verified inpartition_lease_store.go) mean no consumer in that group can acquire the partition until the row is removed, so the stage is starved deterministically with zero service changes. The plant is a plain INSERT that fails loudly if the partition is already leased, so holds must be created before the stage first consumes (pre-hold contract). Granularity is per controller x partition.Release()deletes the row (idempotent after success, retryable on failure) and consumption resumes on the service's next discovery tick. Held messages are never delivered, so there is no retry-budget or DLQ interaction.Stage catalog
test/e2e/submitqueue/stage_test.goenumerates every queue consumer controller across the three services — all 27 consumers: gateway (1), orchestrator (12 primary + 12 DLQ), runway (2) — with aholdStage()suite helper, so any controller x partition can be starved from a test.Lease-semantics contract test
test/integration/extension/messagequeue/mysql/stagehold_test.gopins the guarantees StageHold relies on: a held partition starves the group's consumer while other partitions keep flowing, releasing the hold resumes delivery, andReleaseis idempotent.Deterministic cancel test (reimplemented)
TestCancel_BeforeBatch_ReachesCancellednow parks the pipeline with a StageHold on runway's merge-conflict-check consumer (partition = queue name) instead of stopping/starting the runway container: park (confirmed by a pushed event), cancel, await the pushed "cancelled" event, assert the terminal store state and that no batch ever enrolled the request, then release the hold and assert runway's late signal does not resurrect it. TheconnectRunwayre-dial helper is gone.No on-demand service lifecycle
ComposeStack.StartServiceis removed — services start once inSetupSuiteand stop once inTearDownSuite; there is no start/stop lever mid-suite anymore.StopServiceis retained for the graceful-shutdown verification in teardown.Also in the suite: runway is first-class — gRPC ping test, graceful-shutdown exit-code check, and happy-path assertions that runway answered both the merge-conflict check and the merge with SUCCEEDED (the assertion that exposed the merge topic collision fixed in #348).
🤖 Generated with Claude Code