perf(worker): index request_nonces(created_at) to stop full-scan sweeps (#1564)
## Description `authorizeRequest` prunes replay-protection nonces on every authenticated request via `DELETE FROM request_nonces WHERE created_at < ?` (`packages/core/src/coordinator-api.ts:202`). There was no index on `created_at` โ the primary key is `(device_id, nonce)` โ so that DELETE scanned the entire live nonce window to remove the roughly one row that had expired. D1 bills scanned rows as `rows_read`. Because the table's steady-state size is itself proportional to request volume (600s retention, `DEFAULT_TIME_WINDOW_S * 2`), daily reads grew with the **square** of traffic. That is why the 30-second status cache from #1512 did not deliver the reduction its design predicted: it lowered request volume, but the per-request cost kept rising with the volume that remained. Production `wrangler d1 insights`, top query by reads: | Query | Runs | Avg rows read | Total rows read | Share | |---|---|---|---|---| | `DELETE FROM request_nonces WHERE created_at < ?` | 24,909 | 179 | 4,462,989 | 92% | | `listGroupPeers` | 10,721 | 12 | 128,652 | 2.6% | | `listScopeMemberships` | 13,984 | 8 | 124,391 | 2.6% | | `listScopes` | 9,450 | 7 | 70,262 | 1.4% | | `listInvites` | 1,507 | 45 | 69,213 | 1.4% | That single query is ~89% of the 5,000,000-row daily free-tier cap, and it read 4.46M rows to delete 26,250. Cloudflare scores its `queryEfficiency` as 0. This adds one index. `EXPLAIN QUERY PLAN` against the real schema goes from: ``` SCAN request_nonces ``` to: ``` SEARCH request_nonces USING INDEX idx_request_nonces_created_at (created_at<?) ``` so the sweep touches only already-expired rows instead of the whole window. The migration is additive and backward-compatible. The deployed Worker keeps serving against the upgraded schema with no code change and no redeploy, the same way the 0.40.2 migrations were applied. Deliberately out of scope: the daemon's duplicate `/v1/peers` lookup per tick, and the `GET /v1/scopes` N+1 through `requesterAuthorizedForScope`. Both are real, both are worth fixing, and neither is a quota problem โ the insights data puts every non-nonce query combined at ~8% of reads. `coordinator_reciprocal_approvals` holds 17 rows and never reached the top five. ## Type of Change - [ ] ๐ Feature (new functionality) - [x] ๐ Bug fix (fixes an issue) - [ ] ๐ Documentation (docs-only change) - [ ] ๐ง Maintenance (refactor, chore, CI, etc.) - [ ] ๐งช Testing (test-only changes) ## Testing - [x] Relevant checks pass locally (`pnpm run tsc`, `pnpm run lint`, `pnpm run test`) - [ ] Added/updated tests for changes - [x] Manually verified changes work as expected `pnpm run tsc` and `pnpm run lint` are clean. `pnpm run test` reports 5,731 passed with one unrelated environmental failure: `packages/mcp-server/src/http.test.ts` cannot bind `::1` in this container. The schema-parity suites that load `schema.sql` directly โ `packages/core/src/d1-coordinator-store.test.ts` (89 tests) and `packages/cloudflare-coordinator-worker/src/index.test.ts` (12 tests) โ both pass. Query plans were verified by loading the committed `schema.sql` into SQLite with and without the new index, seeded with 179 nonces spread across the 600-second retention window to match production. ## Checklist - [x] Code follows project style (`pnpm run lint` passes for touched files) - [x] Self-review completed - [x] Documentation updated (if needed) - [x] No new warnings introduced ๐ค Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Lq71RnJbURWvF9fmkKuDRc --- _Generated by [Claude Code](https://claude.ai/code/session_01Lq71RnJbURWvF9fmkKuDRc)_
A
Adam Kunicki committed
e29a27700a00709be16fbe90c3032f1467797344
Parent: 504b452
Committed by GitHub <noreply@github.com>
on 9/1/2026, 10:08:02 PM