86 lines
8.0 KiB
Markdown
86 lines
8.0 KiB
Markdown
# Performance Baseline — Phase 02 Quickwins (Task #36)
|
|
|
|
This document tracks the expected and observed effect of the 8 quickwin
|
|
optimizations shipped under task #36
|
|
(`docs/improvement-plan/02-observability/performance-quickwins.md`).
|
|
|
|
## Status
|
|
|
|
- **Code-level changes:** shipped to `main` 2026-05-17 (8 atomic commits +
|
|
this baseline doc).
|
|
- **`/metrics` endpoint status:** code shipped for API and worker
|
|
runtimes. The API exposes token-protected `GET /metrics` when
|
|
`METRICS_ENABLED=true`; worker admin servers expose token-protected
|
|
`/metrics` when enabled and keep health probes on a separate port.
|
|
- **Live `/metrics` baseline:** operator-owned. The endpoint code exists,
|
|
but the live before/after numbers still require an environment with
|
|
`METRICS_ENABLED=true`, `METRICS_TOKEN`, scrape access and a controlled
|
|
load run.
|
|
|
|
## Per-item expected gains (code-level reasoning)
|
|
|
|
| Item | File | Mechanism | Expected gain |
|
|
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| 1. N+1 in admin users list | `apps/web/app/routes/admin/accounts/users.tsx` + `apps/web/app/utils/db/logs.db.server.ts` | Replace N `findFirst` calls with 1 `groupBy` + 1 `findMany` | DB queries on the admin users-list loader: `O(N)` → `O(1)` (101→2 for a 100-user page). Largest practical win on this route. |
|
|
| 2. Duplicate tenant fetch | `apps/web/app/utils/session.server.ts` + `apps/web/app/routes/admin/accounts/users.tsx` + `apps/web/app/routes/oauth/github/callback.tsx` | `setLoggedUser` now returns `defaultTenant`; callers reuse it instead of fetching again | One fewer `getTenant()` round-trip per sign-in / impersonation start (≈ 1 DB query/login). |
|
|
| 3. S3 client + signer caching | `apps/api/src/storage.ts` | Memoize S3 storage adapter, signed-URL adapter and per-day SigV4 signing key by SHA256-of-config | Eliminates per-call adapter construction + HMAC chain (4 HMAC-SHA256 ops) on every signed URL. Per-request CPU win in hot upload/download paths. |
|
|
| 4. clearSubscriptionsCache thrash | `apps/web/app/utils/services/.server/subscriptionService.ts` + `apps/web/app/utils/db/subscriptions/tenantSubscriptionProducts.db.server.ts` | New `clearTenantSubscriptionCache(tenantId)` replaces a global wipe in `cancelTenantSubscriptionProduct` | Subscription-change webhooks no longer evict every tenant's cached subscription. Tenant N=1000 → only 1 eviction per event instead of 1000. |
|
|
| 5. CSRF header-first check | `apps/web/app/utils/session.server.ts` | Already-resolved in commit `83a57f1c`; regression test added | No additional perf change in #36; documents the historical fix and now-enforced contract: 0 body-clones when the header carries the token. |
|
|
| 6. Recovery-code rejection sampling | `apps/web/app/utils/auth/mfa/recovery-codes.server.ts` | Drop `while(set.size < n)` loop; generate batch + max 2 retries | Generates 10 codes in one batch (≥99.999% of calls); throws on systemic failure instead of looping forever. Bounds wall-clock generation cost. |
|
|
| 7. UUID collision loop | `apps/api/src/auth/session/session-store.ts` | `while (sessions.has(sessionId))` → single insert; throw on collision | 1 ID generation per `createSession` instead of unbounded retry. UUID-v4 has 122 bits of entropy, so a collision is a systemic error to surface, not mask. |
|
|
| 8. CSRF token size | `apps/web/app/utils/session.server.ts` | `randomBytes(100).toString("base64")` → `randomBytes(32).toString("hex")` | Token shrinks ~136 chars → 64 chars. Saves ~70 bytes/cookie and ~70 bytes/response per CSRF-bearing endpoint. Aggregate response-body reduction across the cluster. |
|
|
|
|
## Test coverage added
|
|
|
|
- `apps/web/app/utils/db/logs.db.server.test.ts` — `getLastUserLogsByUserIds`
|
|
uses exactly 1 `groupBy` + 1 `findMany`.
|
|
- `apps/web/app/utils/session.server.test.ts` — `setLoggedUser` returns
|
|
`defaultTenant`; `validateCSRFToken` never clones the request body when
|
|
the header carries the token; `generateCSRFToken` emits 64 hex chars.
|
|
- `apps/api/src/storage.test.ts` — repeated config hits the adapter cache;
|
|
bucket change misses; secret rotation misses.
|
|
- `apps/web/app/utils/services/.server/subscriptionService.test.ts` —
|
|
`clearTenantSubscriptionCache` does not enumerate other tenants;
|
|
`clearAllSubscriptionsCache` invalidates all tenant entries.
|
|
- `apps/web/app/utils/auth/mfa/recovery-codes.server.test.ts` — generation
|
|
completes under a strict wall-clock bound (regression guard against
|
|
unbounded rejection sampling).
|
|
- `apps/api/src/auth/session/session-store.test.ts` — UUID collision now
|
|
throws instead of silently retrying.
|
|
|
|
## Live baseline (operator-owned)
|
|
|
|
Populate the table below by running `oha -n 5000 -c 50` against staging
|
|
twice for a comparable before/after window and scraping API/worker
|
|
`/metrics` snapshots with `Authorization: Bearer <METRICS_TOKEN>`.
|
|
The code path is shipped; the live baseline values are operational
|
|
evidence and should be recorded only from the target environment.
|
|
|
|
| Metric | Before | After | Delta |
|
|
| ----------------------------------------------- | --------------- | ----- | ----- |
|
|
| p50 latency, GET `/admin/users` | TBD | TBD | TBD |
|
|
| p95 latency, GET `/admin/users` | TBD | TBD | TBD |
|
|
| Prisma query count / request (admin users list) | TBD | TBD | TBD |
|
|
| p95 latency, signed-URL endpoint | TBD | TBD | TBD |
|
|
| p95 latency, login flow | TBD | TBD | TBD |
|
|
| Cache evictions per Stripe webhook | TBD | TBD | TBD |
|
|
| CSRF cookie size (bytes) | ~136 hex equiv. | 64 | -53% |
|
|
|
|
## Notes
|
|
|
|
- All eight items shipped as **atomic** commits; any single item can be
|
|
reverted in isolation.
|
|
- Item 5 was already in tree from `83a57f1c fix: enforce csrf on settings
|
|
mutations`. It is recorded here for completeness and now carries an
|
|
explicit no-body-read assertion.
|
|
- Path corrections were necessary on items 2 and 6 — see the per-item
|
|
Progress Log entries in
|
|
`docs/improvement-plan/02-observability/performance-quickwins.md`.
|
|
|
|
## Footer
|
|
|
|
- Date: 2026-05-17
|
|
- Owner: Phase-02 quickwins agent
|
|
- Live-test hardware: operator-owned (fill with the target environment
|
|
used for the controlled load run)
|