feat: alarm and test for order tracking silently stopping - #698
Open
codyborn wants to merge 1 commit into
Open
Conversation
Order tracking stopped starting for every chain for a day and nothing detected it. Three independent gaps let that happen; this closes all three. 1. No alarm on the only signal that existed. UniswapXOrderService swallows post-persist failures on purpose so an accepted order is never reported as rejected, so a broken tracker leaves requests returning 201 and no error-rate alarm moving. PostOrderPostPersistFailure was emitted but appeared exactly once in the repo — nothing alarmed on it. Adds a SEV3. 2. No alarm for tracking stopping entirely. The step function alarms are rate alarms over failed/started with TreatMissingData.IGNORE, so with zero executions the expression has no datapoints and they hold their last state — OK. They detect "executions are failing" and are blind to "executions stopped happening". Adds a SEV2 heartbeat on a new OrderTrackerStarted counter with TreatMissingData.BREACHING. Alarms on our own counter rather than AWS/States ExecutionsStarted because that metric is per-state-machine and summing one per chain exceeds CloudWatch's 10-metric cap for math-expression alarms (21 chains today). Prod-only: beta has no steady organic order flow, so an hourly heartbeat there would alarm on idleness rather than breakage. 3. No test of the CDK. bin/ had no test coverage at all, so a map that was declared but never populated type-checked, synthesized, and deployed. Adds synth tests covering both sides of the seam: StepFunctionStack publishes a name for every supported chain and stays key-aligned with the ARN map, and LambdaStack's STATE_MACHINE_NAMES contains no "undefined", has an entry per chain, and never regresses to the per-chain env var shape that exhausted the 4KB limit. Verified the tests fail for the right reason: removing the name-map assignment fails exactly 3 of them and nothing else. Tests: 558 pass, 14 pre-existing failures unchanged (field-validator / order-validator LABS_COSIGNER). tsc clean, eslint no new errors.
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.
Order tracking stopped starting on every chain for a day and nothing detected it. Three independent gaps allowed that; this closes all three.
1. No alarm on the only signal that existed
UniswapXOrderServiceswallows post-persist failures deliberately — an accepted order must never be reported as rejected. So when tracking breaks, requests still return 201 and no 4xx/5xx alarm moves.PostOrderPostPersistFailurewas emitted for every order on every chain and appeared exactly once in the repo: theputMetriccall. Nothing alarmed on it, nothing dashboarded it.Adds a SEV3 on it.
2. No alarm for tracking stopping entirely
The step function alarms are rate alarms over
100*((throttled+failed+timedOut+aborted)/started)withtreatMissingData: IGNORE. When zero executions start, the expression has no datapoints andIGNOREholds them at their last state — OK. They detect "executions are failing" and are structurally blind to "executions stopped happening", which is what a broken ARN or bad deploy actually looks like.Adds a SEV2 heartbeat with
TreatMissingData.BREACHING, mirroring the existingLoopCompletedalarm instatus-stack.Two design notes:
OrderTrackerStartedcounter, notAWS/StatesExecutionsStarted. The AWS metric is per-state-machine, and summing one metric per chain hits CloudWatch's 10-metric cap for math-expression alarms — I tried it and CDK rejects it outright at 21 chains. Our own counter is aggregate by construction and needs no maintenance as chains are added.3. No test of the CDK at all
bin/had zero test coverage, so a map that was declared but never populated type-checked (an index signature returnsstring, notstring | undefined, withoutnoUncheckedIndexedAccess), synthesized, and deployed.Adds synth tests covering both sides of the seam:
StepFunctionStackpublishes a name for every supported chain, and the name map stays key-aligned with the ARN map.LambdaStack'sSTATE_MACHINE_NAMEScontains no"undefined", has an entry per chain, and never regresses to the per-chainSTATE_MACHINE_ARN_<chainId>shape that exhausted the 4KB limit in the first place.Verified they fail for the right reason: removing the name-map assignment fails exactly 3 of them and nothing else. A guard nobody has watched go red isn't a guard.
Tests
558 pass. The 14 remaining failures are pre-existing — clean
mainreports the identical 14 (field-validator/order-validator,LABS_COSIGNER).tscclean, eslint no new errors.On CD gating — worth reading before adding more
I went in expecting to have to add integ tests and gate CD on them. Both already exist:
bin/app.ts'saddIntegTestsadds a post-deployCodeBuildSteprunningyarn test:e2eto both the beta and prod stages viaaddPost. A beta failure fails the pipeline, so prod promotion is already blocked.order.test.ts:543expects an order to reachexpired, and:588/:602expectfilled.waitAndGetOrderStatus's own comment says "We have to wait for the sfn to fire."So this bug should have failed beta e2e and blocked prod. That leaves an open question I could not answer without pipeline access: did the e2e run and fail (in which case CD worked and only beta was affected), or did something let it through? Worth confirming before adding redundant gating.
What the synth tests above genuinely add is speed and locality: they catch this class in CI in seconds, pre-deploy, instead of after a beta deploy plus a four-minute e2e wait.
🤖 Generated with Claude Code