Skip to content

Commit 86b05a6

Browse files
valvesssclaude
andcommitted
HAULDR-1: falha do realtime não pode deixar o cutover pela metade
O stageA exclui o schema `realtime` (-N realtime) apostando que re-registrar o tenant o reconstrói. Isso vale quando o tenant é CRIADO; no cutover ele já existe (veio do provisionamento no pg16), então o Realtime só atualiza o db_host e PULA as migrations per-tenant. O cluster novo fica com o schema `realtime` vazio e o applyRealtimeRls morre em `relation "realtime.messages" does not exist`. A premissa está escrita em comentário no realtime.ts e é falsa no re-registro. Isso abortava o stageB DEPOIS do flip do cluster: o ufc_dev ficou com registro=pg17, auth/rest no v17 e o tenant do pooler ainda no pg16 — meio migrado, que é o pior estado possível. Realtime está provisionado e MORTO na frota: 0 subscriptions e 0 messages em todos os 8 projetos habilitados; o único slot de replicação em uso é do PowerSync (db_maglink, que fica no pg16). db_kelvin e db_zyramed já estavam no v17 com 0 tabelas em `realtime` e ninguém sentiu. Logo um realtime quebrado não custa nada hoje, e um cutover pela metade custa o app. Passa a avisar alto e terminar a mudança. Reprovisionar realtime no pg17 exige dropar e recriar o tenant p/ as migrations rodarem — dívida separada. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 6794515 commit 86b05a6

1 file changed

Lines changed: 25 additions & 1 deletion

File tree

control-plane/src/cutover.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,31 @@ export async function stageB(name: string): Promise<void> {
399399
// REST is à-la-carte — only repoint it if the project has it.
400400
if (await hasRest(name)) await provisionRest(name);
401401
// Realtime is à-la-carte + service-configured — only if enabled and registered.
402-
if (realtimeEnabled() && (await hasRealtime(name))) await provisionRealtime(name);
402+
//
403+
// Non-fatal, deliberately. stageA excludes the service-managed `realtime`
404+
// schema (`-N realtime`) on the premise that re-registering the tenant rebuilds
405+
// it — true when a tenant is CREATED, false here: the tenant already exists
406+
// from the pg16 provisioning, so Realtime updates its db_host and skips the
407+
// per-tenant migrations. The new cluster is left with an empty `realtime`
408+
// schema and `applyRealtimeRls` dies on the missing `realtime.messages`.
409+
//
410+
// Letting that abort stageB would strand the project half-moved (cluster
411+
// flipped, tenant not). Realtime is provisioned-but-unused fleet-wide (0
412+
// subscriptions and 0 messages in every enabled project; the one replication
413+
// slot in use belongs to PowerSync), so a broken realtime costs nothing today
414+
// while a stranded cutover costs the app. Warn loudly and finish the move;
415+
// re-provisioning realtime on pg17 needs the tenant dropped and recreated so
416+
// the migrations run — tracked separately.
417+
if (realtimeEnabled() && (await hasRealtime(name))) {
418+
try {
419+
await provisionRealtime(name);
420+
} catch (e) {
421+
console.warn(
422+
`[cutover ${name}] ⚠️ realtime NOT provisioned on pg17 (${(e as Error).message}) — ` +
423+
`the project moves anyway; realtime stays broken until its tenant is recreated`,
424+
);
425+
}
426+
}
403427

404428
console.log(`[cutover ${name}] stageB 3/4 repoint Supavisor tenant`);
405429
await repointTenant(name, "pg17");

0 commit comments

Comments
 (0)