You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Browse filesBrowse the repository at this point in the historyBrowse files
authored
fix: cut Get Orders read amplification on hot GSI partitions (#696)
* fix: cut Get Orders read amplification on hot GSI partitions
GET /dutch-auction/orders is throwing "Throughput exceeds the current
capacity of your table or index" in prod. The Orders table is
PAY_PER_REQUEST, so there is no table-level ceiling to hit -- the limit
being reached is the ~3000 RCU/s DynamoDB caps a single partition at.
Filler polling concentrates on one GSI partition key (`1_open` on
chainId_orderStatus-createdAt-all), and two multipliers sit on top of it.
Bound the retry loop: MAX_QUERY_RETRY 10 -> 3. `orderType` is applied as
a post-query filter rather than a key condition, so each retry is another
full 50-item page read against the same partition, billed in full even
when it matches no rows. Worst case per request drops from 11 reads to 4.
Add a 250ms cache over the list-query path. Fillers poll the same
chainId/orderStatus shapes continuously, so repeats inside that window
are byte-identical; collapsing them cuts reads on the hot key by roughly
TTL/handler-latency. The key covers every input that can change the
result set, and callers get a copied array so a cached entry cannot be
mutated downstream. Hit/miss metrics are emitted so the real rate is
visible before deciding whether a shared cache is also needed.
The cache is opt-in per repository, not a repository default: the
unimind cron writes orders and re-reads them expecting fresh data, so
only the read-only get-orders path passes it in. TTL is configurable via
GET_ORDERS_CACHE_TTL_MS, and 0 disables it without a code change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: address review — cache entry expiry, kill switch, retry visibility
Stamp cache entries from when the query finished rather than when it
started. A throttled partition retries with backoff, so a query can
outlast the 250ms TTL; dating the entry from the query start wrote it
already expired and silently disabled the cache under exactly the load
it exists to absorb. Covered by a regression test that fails against the
previous behaviour.
Plumb GET_ORDERS_CACHE_TTL_MS through the beta and prod stage envVars.
CDK writes the complete Environment.Variables map on every deploy, so a
console-set variable it does not know about is dropped by the next one —
the documented rollback path did not survive a deploy.
Emit GetOrdersRetryExhausted when a type-filtered query gives up with a
cursor still outstanding, so the cost of the lower MAX_QUERY_RETRY is
measurable rather than assumed.
Correct the copyQueryResult comment: the copy is shallow, so it protects
the array but not the order objects, which must be treated as read-only.
Build the query-cache tests on a locally constructed QueryCache instead
of the exported singleton, whose TTL comes from the environment.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: address review round 2 — dedupe multi-status merges, per-endpoint caches, drop retry cut
- Dedupe getOrdersForMultipleStatuses by orderHash: per-status sub-queries are
independent cache entries, so one can serve a stale page listing the same order
under a second status; keep the non-OPEN copy since transitions flow away from OPEN
- Opt the read-only GET /limit-orders lambda into the query cache — it polls the
same hot single-status GSI partitions on the LimitOrders table
- Move cache construction out of the generic repository into per-endpoint modules;
hit/miss metrics are now named by the cache instance (GetOrdersQueryCache*,
GetLimitOrdersQueryCache*) instead of hardcoded in the shared base class
- Gate cache bookkeeping on enabled so the GET_ORDERS_CACHE_TTL_MS=0 kill switch
also silences the miss metric (a disabled cache no longer looks broken), and skip
key building entirely on uncached repositories
- Revert MAX_QUERY_RETRY to 10 and drop the GetOrdersRetryExhausted metric: the
cache absorbs the hot-partition read amplification, and the deeper retry budget
keeps type-filtered pages full for cursor-ignoring pollers
- Extract the five duplicated per-type retry loops into fetchOrderPages
- Document GET_ORDERS_CACHE_TTL_MS in CLAUDE.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: fix comments
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Alan Wu <60207036+alanhwu@users.noreply.github.com>
Co-authored-by: Alan Wu <alanwu100@gmail.com>
Copy file name to clipboardExpand all lines: CLAUDE.md
+3Lines changed: 3 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -46,6 +46,9 @@ Required for deployment:
46
46
-`RPC_HEADER_SECRET` - Value sent as the `x-internal-service-secret` header on all RPC requests (see `RPC_HEADERS` in `lib/util/constants.ts`). Omitted when unset.
-`GET_ORDERS_CACHE_TTL_MS` - TTL for the read-path query cache on the get-orders/get-limit-orders Lambdas (default 250; set to `0` to disable the cache).
0 commit comments