perf(admin): load the meta schema on first request, not at boot - #554
Open
JonasJesus42 wants to merge 1 commit into
Open
JonasJesus42 wants to merge 1 commit into
JonasJesus42 wants to merge 1 commit into
Conversation
`createAdminSetup` documented its `meta` option as "Lazy loader for admin
meta schema — only fetched when admin requests it", and then invoked the
thunk at module init. So every isolate, on boot, imported the schema, ran
composeMeta over it, and pinned the composed graph on globalThis for its
whole life — for traffic that will never touch `/live/_meta`. The schema
covers every section and every app; exactly one route reads it.
This is the decofile bug in a second place: a large JSON graph made
permanently reachable from a module-level binding.
`createAdminSetup` now registers the loader (`setMetaLoader`) and the
first `/live/_meta` resolves it. Concurrent first requests share one
in-flight load. A failed load is not latched — it answers 503 and the next
request retries, so a transient chunk-load failure can't disable the admin
for the isolate's life. `setMetaData` still works for sites that set the
schema explicitly, and invalidation only drops the cached graph when a
loader exists to rebuild it (clearing it otherwise would 503 permanently).
Second half: the vite plugin now emits `meta.gen.json` as
`JSON.parse("...")` on SSR, the same treatment blocks.gen gets. V8's JSON
parser is the faster path, but the reason here is memory — the schema
stays ONE string until something calls the parse, so an isolate that never
serves the admin never materializes the object graph at all.
`handleMeta` becomes async. Every caller already awaited it except one
line in the Next route handlers, fixed here; the worker-entry interface
type widens to `Response | Promise<Response>` so a site passing the real
handler still typechecks.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problema
createAdminSetupdocumenta a opçãometacomo "Lazy loader for admin meta schema — only fetched when admin requests it". E aí, na linha 47:Isso roda no boot de todo isolate. O schema inteiro — JSON Schema de cada seção e cada app — é importado, passa por
composeMeta, e o resultado fica pinado emglobalThis.__deco_meta_datapela vida do isolate. Para tráfego que nunca vai tocar/live/_meta, que é a única rota que lê isso.É o bug do decofile numa segunda casa: grafo JSON grande permanentemente alcançável por um binding de módulo. O próprio
plugin.js:570já documentava o sintoma ("setup.ts imports meta.gen.json EAGERLY via createAdminSetup") sem tratá-lo como problema.Mudanças
Lazy de verdade.
createAdminSetupregistra o loader (setMetaLoader); o primeiro/live/_metaresolve. Requests concorrentes compartilham um único load em voo. Falha não latcha — responde 503 e o próximo request tenta de novo, senão uma falha transitória de chunk-load desabilitaria o admin pela vida do isolate.setMetaDatacontinua funcionando para quem seta explícito, e a invalidação só descarta o grafo quando existe loader para reconstruir (descartar sem loader daria 503 permanente).meta.gen.jsonviraJSON.parse("...")no SSR, mesmo tratamento doblocks.gen. O parser JSON do V8 é o caminho rápido, mas o motivo aqui é memória: o schema fica como uma string até alguém chamar o parse — então um isolate que nunca serve admin nunca materializa o grafo.Quebra que isso causa
handleMetavira async. Todos os callers já esperavam promise, exceto uma linha empackages/nextjs/src/routeHandlers.ts:60— corrigida aqui, e só isso foi tocado no Next. O tipo da interface emworkerEntry.ts:222alarga paraResponse | Promise<Response>, senão um site passando o handler real quebraria no typecheck.Testes
10 casos. O que fixa a propriedade que interessa: o loader não é chamado no setup. Mais: chamado uma vez no primeiro request e só; um load compartilhado entre requests concorrentes; o schema servido é o composto (
framework, que é o próprio sentinel docomposeMeta); 503 → retry após falha;setMetaDataexplícito sobrevive à invalidação; recomposição após invalidação quando há loader; 304 comIf-None-Match.🤖 Generated with Claude Code
Summary by cubic
Loads the admin meta schema on the first
/live/_metarequest instead of at boot. PreviouslycreateAdminSetupinvoked the meta thunk during setup, so every isolate imported the full schema, composed it, and pinned the result onglobalThisfor its lifetime — even for traffic that never serves the admin.Now setup registers the loader, and the first
/live/_metarequest resolves it. Concurrent first requests share one in-flight load; a failed load answers 503 and retries on the next request without latching; and explicitly-set data viasetMetaDatastill survives invalidation. The Vite SSR plugin also emitsmeta.gen.jsonasJSON.parse("..."), keeping the schema as one string until something calls the parse.handleMetabecomes async: the only non-awaited caller, inpackages/nextjs/src/routeHandlers.ts, is updated, andAdminHandlerswidens to acceptResponse | Promise<Response>.Written for commit bd86943. Summary will update on new commits.