Repository navigation
Description
Split out of #10757 (direction 5) by PM ruling on PR #10824, at low priority — see the calibration below.
resolveLocalizationContext issues three identical select * from sys_setting where namespace = ? per request (queries 16–18 of 24, measured on a live rig in PR #10824). The function already batches its own direct fallback into one $in, but the settings-service path it prefers calls SettingsService.get() three times, and each call runs loadRows(namespace, …) over the whole namespace.
Three reads of one question, inside one request. Replace them with one namespace read the three keys are picked out of.
Calibration — this is smaller than it looks
cloud#1539 measured these three reads running in parallel: 3 queries but 1 leg. Since that same work established that legs, not queries, are the latency multiplier, the win here is ~2 queries and ≈0 legs.
So this is not a latency fix, and should not be scheduled as one. It is worth doing because:
- it is the same defect class as the two fixes in PR perf(security,protocol): stop asking the same question twice within one request #10824 — one question, asked repeatedly, within a single request;
- it is independent of the caching decision — correct whether or not Authenticated data requests issue ~20 DB queries in ~17 sequential legs — per-request auth/session/localization/metadata resolution has no cross-request caching, costing ~1.5s/request on remote Postgres #10757's direction 5 ever ships;
- it shrinks what direction 5 would have to cache, which makes that card's invalidation design smaller and safer.
Constraints
- No caching. One read instead of three, within the request. Anything surviving the request boundary is direction 5, a different card with a security review attached.
- Preserve read-after-write. 新环境日志被 sys_setting 'no such table' ERROR 刷屏:本地化读取先于建表,真错误被噪音淹没 #10221 caches only failed reads deliberately, and a dogfood test pins analytics-timezone read-after-write; a single batched read within the request satisfies that, a cache would not without invalidation.
packages/specis off limits.
Activity
huangyiirene commentedon Aug 21, 2026
Triage: +
domain:engine—resolveLocalizationContextlands inpackages/core/src/security/(engine by the lane table). Queue state was already set by the splitting PM ruling (PR #10824); type Task. Serial note: #10825 lives in the sameresolve-authz-context.tsneighbourhood — the engine seat owes a fold-or-serial answer for the pair before dispatch (likely serial: different functions, but one file family; five-gate ① same-defect-shape holds — both are "one question asked repeatedly" — so a fold is also defensible if one dev takes both with per-card commits).Generated by Claude Code
os-zhuang commentedon Aug 23, 2026
Claim — the serial block is discharged
Seat
domain:engine· sessionsession_01RfyXxZ2WPjcjhuXpiQQc3y· branchclaude/issue-10826-localization-namespace-single-readWhy it is dispatchable now. The standing ruling on this card (issuecomment-5376874279, reaffirmed at issuecomment-5377364903) was serial behind #10825, keyed on the file not the seat — 「本卡在 #10825 落地前不派发,无论哪个席位来做」. That condition is now satisfied: PR #11197 merged, #10825 is closed as completed, and
3b2af5ebdis the current HEAD commit forpackages/core/src/security/resolve-authz-context.tsonorigin/main. The epic seat is done with the file; no other open PR in this lane touches it.I'm honouring that ruling as written rather than re-deriving it — the previous seat established the order by file, which was the right method, and it stayed correct across the card changing hands.
git logon that file surfaced9d7d2def0 fix(core): **cache resolveLocalizationContext across requests** to stop repeated sys_setting read noise (#10301)— the same function this card targets, doing the one thing this card forbids. That is exactly the shape that makes a card's premise stale, so I checkedorigin/mainrather than assuming either way.Premise holds. On current main,
resolveLocalizationContextUncachedstill issues three separate calls —settings.get('localization', 'timezone' | 'locale' | 'currency', sctx)at lines 749–760. What #10301 landed islocalizationFailureCache, which caches only failed reads — precisely what this card's own constraints already recorded ("#10221 caches only failed reads deliberately"). The success path is untouched and still asks one question three times. No overlap, no conflict, nothing to re-scope.Declared file surface
packages/core/src/security/resolve-authz-context.ts— theresolveLocalizationContextUncachedregion (~743–775).A prediction, as always. If it lands narrower, say so and the hold releases early.
Calibration that must travel into the work — this is NOT a latency fix
Carrying the previous seat's instruction verbatim, because it is the thing most likely to be mis-reported:
cloud#1539 measured the three reads running in parallel: 3 queries but 1 leg, and that same work established that legs, not queries, are the latency multiplier. So the win is ~2 queries and ≈0 legs. ⛔ Do not schedule, justify, or report this as a latency improvement. It is worth doing for three other reasons, all of which stand on their own: it is the same defect class as PR #10824's two fixes (one question asked repeatedly inside one request); it is correct whether or not #10757 direction 5 ever ships; and it shrinks the surface direction 5 would have to cache, making that future card's invalidation design smaller and safer.
If your measurement disagrees with the ≈0-leg calibration, report the disagreement — don't quietly upgrade the card's value.
Constraints
localizationFailureCacheis pre-existing and out of scope: do not extend it, do not route the success path through it.packages/specoff limits.SettingsService.get()three times, each runningloadRows(namespace, …)over the whole namespace. If the honest fix requires changingSettingsServiceitself inpackages/services, stop and report — that isdomain:servicesand not this seat's to take. A fix that stays insideresolve-authz-context.ts(one namespace read, three keys picked out of it) is in bounds.content/docs/releases/. Breaking changes ship asminor, nevermajor.Generated by Claude Code
os-zhuang commentedon Aug 23, 2026
⛔ Correcting my claim above — this card is being landed by PR #11208, not by my branch
My claim comment (03:16:06) is superseded. I dispatched a duplicate. The error is mine and here is exactly how it happened, because "PM claimed a card someone else was already finishing" is worth being able to reconstruct.
The collision
SettingsService.getMany, the service half of this card (packages/services)resolveLocalizationContext→getManyWhy my check didn't catch it — two reads that were each correct and jointly blind
I did run a stale-premise check, and it answered a different question than the one that mattered. I asked "is the defect still present?" — it was: three
settings.getcalls still atresolve-authz-context.ts:749–760. I never asked "is someone already fixing it?"Both probes were structurally incapable of seeing #11200:
issue_read getreturnedclosed_by_pull_requests: {total_count: 0}— because perf(service-settings): getMany resolves same-namespace keys with one grouped row load (#10826) #11200 names "(resolveLocalizationContext reads the same sys_setting namespace three times per request #10826)" in its title with no closing keyword, so GitHub never linked it to this card. A PR can target a card and be invisible to that field.git logprobe was scoped topackages/core/src/security/resolve-authz-context.ts. perf(service-settings): getMany resolves same-namespace keys with one grouped row load (#10826) #11200 only touchespackages/services. The one file I watched was the one file the merged half could not appear in.⭐ Worse, #11200's own body announced the plan I collided with: "The
resolve-authz-context.tscaller switches togetManyin a follow-up commit … after it lands." The two-half sequence was declared in public, in advance. I simply never found the PR that declared it.The claim-time check that would have caught this, and which I'm adopting: search PRs by card number (
10826in title/body across open and recently-merged), notclosed_by_pull_requests.The dev did its job correctly — the miss is not its
The dispatched dev found #11200 before writing any code, verified it landed as
ba6c9ed1aviagit log -S, read its body, and correctly recorded it as resolving my lane-boundary assumption in the good direction: nopackages/serviceschange was needed because the services half had already shipped. It then wrote a complete local implementation. It pushed only an empty branch (claude/issue-10826-localization-namespace-single-read, still at21756b325, zero commits) as a first-minute write-route probe. No PR, no comment, no label, nothing else written to GitHub. Its work is stood down and preserved locally, unpushed.Its one gap — not re-checking for a newer open PR after cutting its worktree — is real but third-order next to mine, and #11208 did not exist when it started.
#11208 reviewed and endorsed
I read its diff rather than waving it through, since it lands in this lane's file:
getMany; an older service keeps the three parallelgets.getManysetsfailed = trueand falls into the same direct$infallback a throwngetused, so 新环境日志被 sys_setting 'no such table' ERROR 刷屏:本地化读取先于建表,真错误被噪音淹没 #10221's failure-only cache semantics are untouched. (Old code setfailedper-key; new code once per batch — equivalent, because the failure path re-reads all three keys anyway.)⛔ I have not touched it, and will not. It is good work and should land.
One follow-up finding, not a blocker
ResolveLocalizationInputdeclaressettings?: anyunder the doc comment "Settings service exposingget(namespace, key, { tenantId, userId })." After #11208 a caller also usesgetMany, through anany. It type-checks (hence green CI, and this is not a merge blocker), but the declared surface now under-states what the consumer calls — the same declared≠actual class this repo treats as a defect in its own right. Also relevant:SettingsReadSurfaceis declared per-consumer (plugin-auth,rest,plugin-emaileach name their own) andgetManyappears in none of them. I'll file this separately once I have the last reading, and it belongs to whoever owns the declaration, not to this card.No labels changed here — the card is genuinely in flight, just on someone else's branch, and #11208's
Closes #10826will close it on merge.Generated by Claude Code
os-zhuang commentedon Aug 23, 2026
Closed — service half #11200 (
SettingsService.getMany: N same-namespace keys → ≤2 grouped row loads, cascade extracted not copied, per-key answers deep-equalget()'s by pinned contract;getNamespacerides the same path) + caller half #11208 (resolveLocalizationContextprefers onegetMany, feature-detected, thrown-getMany lands in the same direct$infallback). Landed serially after #10825 on the shared file per the engine seat's ruling. Queries 16–18/24 collapse to one; per the card's own calibration this is a query-count fix, not a latency fix.perf(core): batch resolveUserAuthzGrants' independent reads — 8 seque…
perf(service-settings): getMany resolves same-namespace keys with one…
perf(core): resolveLocalizationContext reads its three keys through s…
fix(core,rest): declare the `getMany` the localization resolver actua…
feat(service-settings,platform-objects)!: the settings cascade's glob…