Cache the routing decision, not the colour it resolves to - #9
Conversation
routingCache stored the fully resolved queue URL, with the active colour already substituted, in a map with no expiry. The colour changes on every deploy, so an entry that was correct when written silently became wrong -- and since routingCache.get() returns before getActiveColor() is ever reached, the 30s TTL on activeColorCache could never fire for a compiler already cached. The push from the deploy was documented as an optimisation over that TTL while actually being the only mechanism, so a push that did not arrive left a router pointed at the old colour until its process restarted. Cache what the routing table said -- type, plus queueName or targetUrl -- and resolve the colour per request in resolveRouting(). Everything cached is now stable, so caching it forever is correct, and a missed clear costs at most the colour cache's TTL instead of lasting forever. Same public API: lookupCompilerRouting still returns a resolved RoutingInfo, so callers and mocks are unchanged. The four near-identical result/cache blocks collapse into decisionFromItem plus one cache write. This does not remove the need for /admin/clear-cache: a routing-table change mid-deploy is still only invalidated by the push. It removes the permanence of the colour half. See compiler-explorer/infra#2372. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
A concrete trace of what this changes, with values taken from prod: active colour Steady state, before a deploySSM and stores, with no expiry:
The deploy, blue -> greenStep 4 writes What each router does next
Note where the hit returns in the current code: it is the first thing The ALB spreads requests across both routers, so g141 is now version-correct or version-stale roughly 50/50, per request, with no error anywhere. Two hours laterSomeone runs
Half the g141 requests, an hour after a deploy that reported success, triggered by a cleanup command that looks unrelated. With this PR
|
Resolving the colour per request puts getActiveColor on the hot path, and its failure handler returned 'blue'. Before, that catch was reachable only on a routing cache miss, so after warmup essentially never; now an SSM throttle or transient would repoint every request at blue, and with green active that is environment-wide misrouting to a queue whose workers may be scaled to zero. Serve the colour we last read instead, and hold it for another TTL so an SSM outage costs one failed call per 30s rather than one per compile. 'blue' stays the answer only when nothing has ever been cached. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Follow-up to compiler-explorer/infra#2372, which fixed the deploy side of this. This is the remaining half.
routingCachestored the fully resolved queue URL, active colour already substituted, in a map with no expiry:The colour changes on every deploy, so an entry that was correct when written silently became wrong. And because
routingCache.get()returns beforegetActiveColor()is ever reached, the 30s TTL onactiveColorCachecan never fire for a compiler already cached — three comments describe/admin/clear-cacheas an optimisation over that TTL while it is in fact the only mechanism. A push that does not arrive leaves a router on the old colour until its process restarts.Change
Cache what the routing table said —
type, plusqueueNameortargetUrl— and resolve the colour per request inresolveRouting(). Everything cached is now stable, so caching it forever is correct, and the existing TTL becomes reachable.RoutingInfoand thelookupCompilerRouting()signature are unchanged, socompiler-explorer-router.tsand the test mocks don't move. Resolving per request is a Map lookup and string manipulation; no extra AWS calls within the TTL. The four near-identical result/cache blocks collapse intodecisionFromItem()plus one cache write, which is most of the deleted lines.What this does and doesn't fix
It does not remove the need for the clear: a routing-table change mid-deploy (
routingTypeflipping, a changedtargetUrl) is still only invalidated by the push, and resolving the colour later does nothing for that. What it removes is the permanence of the colour half — a router the push misses is now wrong for 30 seconds rather than until it restarts.Tests
Three added; two fail against current
main:124 tests pass,
typecheckandbiome checkclean.🤖 Generated with Claude Code