Skip to content

perf(core): batch resolveUserAuthzGrants' independent reads — 8 sequential legs become 4 waves (#10825) - #11197

Merged
os-zhuang merged 1 commit into
mainfrom
fix/batch-authz-grants-10825
Aug 23, 2026
Merged

perf(core): batch resolveUserAuthzGrants' independent reads — 8 sequential legs become 4 waves (#10825)#11197
os-zhuang merged 1 commit into
mainfrom
fix/batch-authz-grants-10825

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

What

Wave 1 issues the five reads that depend only on (userId, tenantId) concurrently — sys_user (memoized, condition byte-mirrors the sequential path), sys_member{user_id}, sys_user_position, sys_member{organization_id}, sys_user_permission_set. The dependent chain (sys_positionsys_position_permission_setsys_permission_set) stays sequential because it is data-dependent. 8 legs → 4 on the fullest path (2 lightest); per cloud#1539's prod model that is ~150–220ms off every authenticated request. No caching — nothing survives a request.

Equivalence proven, not asserted (the card's red line)

resolve-authz-context.batch-equivalence.test.ts: 11-fixture differential matrix, goldens captured from the sequential implementation at main 795ea05a7 (post-#10982). Per fixture: (1) grants envelope deep-equal incl. array order, (2) query multiset identical — same objects/filters/limits/tenancy/isSystem (a widened $in or merged read fails even when the envelope agrees), (3) leg count matches a per-fixture table and beats sequential, with a wave-1-completeness check so a member-by-member await can't silently restore the cost.

#10982 handoff honored (all three items)

Goldens re-captured from current main; the lapsed-own-membership-among-active-peers fixture kept — its job is pinning why the two sys_member reads are not merged into one $or (measured privilege escalation) — with its explanatory text rewritten to cite the 2026-08-22 ruling; the changed value is documented as #10982's, not this batch's.

Numbers

security/ suite 432 passed (17 files) incl. the 34 new differential assertions; tsc error count identical to main (99 — the +1 over the ledger's 98 pre-exists on main). Changeset included.

Closes#10825. #10826 follows serially on this file per os-elon's ruling.

🤖 Generated with Claude Code

…ntial legs become 4 waves (#10825)
cloud#1539 measured (R²=0.9994, causal latency injection) that an
authenticated request is ~23.4 sequential DB legs and that LEGS, not
query count, are the latency multiplier. resolveUserAuthzGrants
contributed 8 of them (legs 6-13), all live reads with no caching.
Wave 1 now issues the five reads that depend only on (userId,
tenantId) concurrently: sys_user (memoized; condition mirrors the
sequential implementation exactly — a fully-seeded API-key principal
still never touches the table), sys_member{user_id},
sys_user_position, sys_member{organization_id} (fellow-org), and
sys_user_permission_set. The dependent chain stays sequential because
it IS dependent: sys_position needs wave 1's position names,
sys_position_permission_set needs those rows' ids, sys_permission_set
needs the union of direct- and position-granted set ids. 8 legs -> 4
on the fullest path, 2 on the lightest. Per the prod model
(server_ms ~= 33 + L x 36.6) that removes ~150-220ms from every
authenticated request.
Equivalence is PROVEN, not asserted (the card's red line):
resolve-authz-context.batch-equivalence.test.ts is a differential
control — an 11-fixture matrix (multi-org, lapsed memberships,
deactivated positions/sets, validity windows, limit truncation at
200/1000, seeded API-key principals, ai_seat both ways) whose goldens
were captured from the SEQUENTIAL implementation at main 795ea05
(post-#10982, per the #10982 handoff on the card: the
lapsed-own-membership fixture's text now cites the 2026-08-22 ruling,
and its job here is pinning why the two sys_member reads are NOT
merged into one $or — that merge is a measured privilege escalation).
Three assertions per fixture: the grants envelope deep-equals
(including array order), the query multiset is identical (same
objects/filters/limits/isSystem — a widened $in or dropped tenancy
filter fails even when the envelope agrees), and the leg count matches
a per-fixture table while beating the sequential count. The two
sys_member reads and the memoized sys_user read remain exactly as they
were; nothing is cached, nothing survives the request.
Takeover of the stale Round-A delegation per the maintainer's
2026-08-23 prompt; os-elon's serial ruling stands (#10826 follows on
this file).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/core, touching 5 documentable anchor(s).

9 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/automation/approvals.mdx(via sys_user_position (literal))
  • content/docs/data-modeling/objects.mdx(via sys_user_permission_set (literal), sys_user_position (literal))
  • content/docs/deployment/tenancy-modes.mdx(via sys_member (literal))
  • content/docs/permissions/administrator-guide.mdx(via sys_user_position (literal))
  • content/docs/permissions/authentication.mdx(via sys_member (literal), sys_user_position (literal))
  • content/docs/permissions/authorization.mdx(via sys_user_permission_set (literal), sys_user_position (literal))
  • content/docs/permissions/delegated-administration.mdx(via sys_member (literal), sys_user_permission_set (literal), sys_user_position (literal))
  • content/docs/permissions/permission-sets.mdx(via sys_member (literal), sys_user_permission_set (literal), sys_user_position (literal))
  • content/docs/permissions/positions.mdx(via sys_member (literal), sys_user_position (literal))

6 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/implementation-status.mdx(via sys_member (literal), sys_user_permission_set (literal))
  • content/docs/releases/v13.mdx(via sys_user_permission_set (literal), sys_user_position (literal))
  • content/docs/releases/v14.mdx(via sys_user_permission_set (literal), sys_user_position (literal))
  • content/docs/releases/v15.mdx(via sys_user_position (literal))
  • content/docs/releases/v16.mdx(via resolveUserAuthzGrants (symbol), sys_member (literal), sys_user_permission_set (literal), sys_user_position (literal))
  • content/docs/releases/v17.mdx(via resolveUserAuthzGrants (symbol), sys_member (literal), sys_user_permission_set (literal), sys_user_position (literal))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/core/src/security/resolve-authz-context.batch-equivalence.golden.json) — pages documenting those are invisible to this run
  • the SDK route bridge reached 45 of 221 client-bound route-ledger rows — the other 176 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage

Coarse fallback — 23 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 52a41b72ee01bc24bdd6ac8eb1a37485eb2b4946packageMentionDocs.

Which tree this was computed on

This run read content/docs from 74670f924eeab48ede7e2c54ef01beb74cb37e6d — the merge of head a1d2b184dd7e1613269bea075d4d974681b05274 into base 52a41b72ee01bc24bdd6ac8eb1a37485eb2b4946, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 74670f924eeab48ede7e2c54ef01beb74cb37e6d && git checkout 74670f924eeab48ede7e2c54ef01beb74cb37e6d
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 52a41b72ee01bc24bdd6ac8eb1a37485eb2b4946 a1d2b184dd7e1613269bea075d4d974681b05274 && git checkout -B drift-repro 52a41b72ee01bc24bdd6ac8eb1a37485eb2b4946 && git merge --no-ff a1d2b184dd7e1613269bea075d4d974681b05274
node scripts/docs-audit/affected-docs.mjs --json 52a41b72ee01bc24bdd6ac8eb1a37485eb2b4946

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 52a41b72ee01bc24bdd6ac8eb1a37485eb2b4946 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling labels Aug 23, 2026
@os-zhuang
os-zhuang added this pull request to the merge queueAug 23, 2026
Merged via the queue into main with commit 3b2af5eAug 23, 2026
32 checks passed
@os-zhuang
os-zhuang deleted the fix/batch-authz-grants-10825 branch August 23, 2026 03:04
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 23, 2026
…ettings.getMany (objectstack-ai#10826) (objectstack-ai#11208)
The caller half of objectstack-ai#10826 (service half: objectstack-ai#11200). One grouped
namespace read replaces three per-key get()s — queries 16-18 of 24 on
the measured rig collapse to one, with per-key answers unchanged by
the service's equivalence contract. Feature-detected: an older service
without getMany keeps the three parallel gets (still one leg — this is
a query-count fix per the card's own calibration, not a latency fix).
A thrown getMany lands exactly where a thrown get did: failed=true and
the direct $in fallback, which reads the same three keys. Serial
constraint honored: landed after objectstack-ai#11197 (objectstack-ai#10825) on this file.
Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xlteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Batch resolveUserAuthzGrants: 8 sequential round trips could be 2-3, with no caching and no staleness

1 participant

@os-zhuang