Skip to content

perf(cms): de-duplicate per-request identity lookups (Request_Context_Bundle) - #288

Draft
khuepm wants to merge 2 commits into
mainfrom
claude/request-caching-7c1a11
Draft

perf(cms): de-duplicate per-request identity lookups (Request_Context_Bundle)#288
khuepm wants to merge 2 commits into
mainfrom
claude/request-caching-7c1a11

Conversation

@khuepm

@khuepm khuepm commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Summary

Implements request caching — a per-request de-duplication cache that lives only for the lifecycle of a single request. When the guard chain needs the same object (the caller's users row, user_sites membership, permission bundle) more than once, it now hits the database once and reuses the cached value for the rest of the request.

This is high-load-cache-readiness Req 10 / task 11 (Request_Context_Bundle). Previously withAuth, withSiteMembership, and withStudioAccess each re-resolved the same identity independently.

What changed:

  • withAuth stashes the users row + user_sites membership it already resolved onto the Hono context (c.set('principal', …)).
  • withSiteMembership reads that bundle before querying, so it no longer re-issues the same users/userSites lookups.
  • withStudioAccess reuses the access bundle withSiteMembership already computed instead of calling PermissionService.bundle() a second time.
  • Every guard falls back to querying when the bundle is absent, so middleware stay independently correct and standalone-testable.

Net effect: a typical authenticated request performs at most one users lookup, one userSites lookup, and one PermissionService.bundle().

Loại thay đổi

  • perf — Cải thiện hiệu năng
  • refactor — Cải thiện code, không thay đổi hành vi (guard semantics unchanged)

Phase / Feature liên quan

high-load-cache-readiness — Phase P1, Requirement 10 (Hợp nhất tra cứu trong middleware chain), task 11.1–11.3.


✅ Definition of Done (DoD)

Code Quality

  • Không có TypeScript error (pnpm -F @lumibase/cms typecheck pass ✅)
  • Tất cả tests pass (pre-commit pnpm test — 299 test files passed, 1 skipped, 0 failures)
  • Self-review: đã đọc lại diff line-by-line

Testing

  • Behavioural matrix test request-context-bundle.test.ts (principal × route → status) — Req 10.3
  • Query-count / de-dup assertions: identity lookups run exactly once; PermissionService.bundle() called exactly once per request — Req 10.4
  • Independence test: each guard still queries on its own when the bundle is absent
  • Tripwire security-guards.wiring.test.ts giữ nguyên pass (Req 10.2)

Security

  • De-dup không nới lỏng quyền — a request rejected before is still rejected with the same status code (asserted in the matrix)
  • Multi-tenant isolation unchanged: cached membership is resolved for the request's siteId; the tenant-membership gate still runs
  • No secrets/credentials logged; cached principal holds only id/externalId/email/isBootstrap + membership roleId

Database / Migrations

  • Không có schema change / migration — thuần middleware + per-request context var

Setup impact

Conventional Commits

  • perf(cms): de-duplicate per-request identity lookups (Request_Context_Bundle)

Rollback Plan

Pure middleware refactor with no schema or config change — revert the commit to restore the previous per-guard lookups. Behaviour is externally identical, so no data or client migration is involved.

Notes for Reviewers

  • The core invariant: reuse the bundle only when it is present; otherwise fall back to the original query. This keeps every middleware correct when mounted alone (as the existing unit tests do).
  • A non-bootstrap principal that reaches withSiteMembership through withAuth has already passed withAuth's membership gate, so a cached membership is authoritative — the query only runs when no bundle was populated.
  • withAuth's users selects were widened by two non-sensitive columns (externalId, email) so the cached row carries everything withSiteMembership needs.

Generated by Claude Code

khuepm added 2 commits July 23, 2026 13:58
…_Bundle)

Implements high-load-cache-readiness Req 10 (task 11): a request-scoped
cache that lives only for one request's lifecycle and eliminates duplicate
DB reads for the same principal.

- withAuth stashes the resolved `users` row + `user_sites` membership on the
  Hono context (`c.set('principal', ...)`) after it resolves them.
- withSiteMembership reads that bundle before querying, so it no longer
  re-issues the same `users`/`userSites` lookups.
- withStudioAccess reuses the `access` bundle withSiteMembership already
  computed instead of calling PermissionService.bundle() a second time.

Each guard falls back to querying when the bundle is absent, so middleware
stay independently correct (and standalone-testable). Guard semantics are
unchanged — a request rejected before is still rejected with the same
status code.

Adds request-context-bundle.test.ts (behavioural principal x route matrix +
query-count de-dup assertions, Req 10.3/10.4); the security-guards.wiring
tripwire is preserved.
The apps/shell row already uses #80; the registry-numbering check requires
unique # ids. Bumps the Request_Context_Bundle row to #81 (new max).

khuepm commented Jul 23, 2026

Copy link
Copy Markdown
Owner Author

Pushed 4f7d400 to address the CI failures on this PR:

✅ "Version, typecheck, tests, and build" — fixed. The failure was in registry:check: my Setup Impact Registry entry reused id #80, which the apps/shell row already holds (# must be unique). Renumbered my row to #81 (the new max). node scripts/check-registry-numbering.mjs now reports 82 rows, all # values unique, and the full test suite is green locally (cms 299 passed / 1 skipped).

⚠️ "Dependency vulnerability audit" — pre-existing on the base branch, not introduced here. The advisories are in next, fast-uri, etc.; this PR's diff touches only middleware TypeScript and spec markdown and changes no package.json / lockfile, so pnpm audit fails identically on main. Remediating those (bumping next ≥16.2.11, fast-uri ≥3.1.3, and the other flagged deps) is a separate dependency-maintenance change, out of scope for this request-caching PR. Happy to open a follow-up for the upgrades if you'd like.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant