SIGN IN SIGN UP

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