Pular para conteúdo

Resetar a replicação PowerSync

Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-09-14

Operação destrutiva

Zera o estado replicado (slot lógico do Postgres + base do PowerSync no MongoDB) e força todos os clientes mobile a um full re-sync. Leia até o fim antes de executar pela primeira vez.

O que é

Endpoint/tela admin que zera o estado replicado e força os clientes a refazer o sync a partir do estado atual das tabelas bi_*. Usado para colapsar o histórico acumulado no Mongo (bucket_data/op_log), que cresce sem parar. São 9 steps (VALIDAR → LOCK → PAUSE → SNAPSHOT_PRE → RESET_SLOT → DROP_MONGO → RESUME → SAMPLE_POST → FINALIZAR) executados pelo NukeReplicationService; o desenho está na etapa 05.

Contrato: apesar do título "Resetar a replicação", o endpoint, o header e as envs mantêm o nome "nuke" (/admin/nuke-replication, X-Confirm-Nuke, POWERSYNC_NUKE_CONFIRMATION) e a chave de sinal permanece ultimo_nuke — são contratos lidos por operadores e pelo cliente Dart; não renomear.

Quando usar

  • Storage do Mongo passou da faixa de tolerância (limpeza mensal/trimestral).
  • Após mudança de schema em bi_* que invalide os checkpoints.
  • Diagnóstico de divergência entre PG e Mongo (último recurso).

Quando não usar

  • Em horário comercial — clientes online sofrem um freeze de alguns segundos; com muitos clientes (50+), o re-bootstrap simultâneo gera carga.
  • Sem janela de baixo tráfego confirmada com operação.
  • Com restart manual (sem COOLIFY_API_TOKEN): sem ter parado o PowerSync antes.

Pré-requisitos

Variáveis no app (tabela completa em deploy):

  • POWERSYNC_MONGODB_URI — sem ela, o reset só simula o drop.
  • POWERSYNC_NUKE_CONFIRMATION — habilita o endpoint REST (a tela /admin não depende dela).
  • Restart automático (recomendado): COOLIFY_API_TOKEN + POWERSYNC_COOLIFY_NAME (ps.onpetro) ou _UUID → a estratégia coolify reinicia o recurso PowerSync sozinha ao fim. Sem o token, o restart é manual. Config incompleta degrada para no-op com WARNING (o reset não quebra, mas o PowerSync não reinicia sozinho).
  • Postgres — aplicar uma vez, como superuser (o step RESET_SLOT usa pg_drop_replication_slot + pg_terminate_backend):
    ALTER ROLE user_onpetro WITH REPLICATION;   -- gerenciar slots
    GRANT pg_signal_backend TO user_onpetro;    -- terminar backend de outra sessão
    
    Sem isso, o reset falha com permission denied to use replication slots. Não dar escrita ao powersync_role (ele é SELECT-only, decisão 0013).
  • PowerSync — o powersync.yaml precisa ter bi_configuracao no stream recent_data (priority 0, sem filtro). Após mudar o sync_rules, redeploy do recurso PowerSync no Coolify — senão o sinal ultimo_nuke é gravado no PG mas nunca chega no cliente.
  • Tabela bi_* nova replica em DOIS passos, e o 2º é EXTERNO a este repo. A migração no processador só faz o GRANT SELECT ao powersync_role (ex. V16 para bi_fornecedor/bi_compra). Para o dado chegar no cliente é preciso, no repo ../powersync/: (1) incluir a tabela na publication powersync (se ela não for FOR ALL TABLES) e (2) adicioná-la ao sync_rules.yaml + redeploy do recurso PowerSync. Sem esse passo, a tabela não replica mesmo com o GRANT aplicado — o bi_compra fica populado no PG mas o Painel de Compras não vê nada.

Pré-requisitos no Coolify (só para o restart automático): Settings → API ligado; token de Keys & Tokens com permissão de escrita/deploy; e (se a COOLIFY_API_URL for a pública) o IP de saída do container na allowlist. Usando a URL interna http://coolify:8080 (recomendado no mesmo daemon), a allowlist é dispensada.

Checklist antes de chamar:

  1. [ ] Janela de baixo tráfego confirmada.
  2. [ ] Só com restart manual: PowerSync parado (docker stop powersync / kubectl scale deploy/powersync --replicas=0). Com coolify, pule.
  3. [ ] POWERSYNC_NUKE_CONFIRMATION configurada (só para o endpoint REST).
  4. [ ] Disparo REST: Bearer em mãos. Disparo UI: acesso à tela /admin.
  5. [ ] Telegram (canal de ops) sendo monitorado.

Passos

Opção A — Tela /admin (recomendado)

  1. Abrir /admin (login Google), clicar em "Resetar replicação PowerSync".
  2. Preencher Motivo e Operador e digitar RESETAR no modal anti-acidente (a palavra destrava o submit). Confirmar.
  3. A tela redireciona para o progresso (/admin/nuke-replication/{id}), que recarrega sozinha a cada 3 s — os 9 steps vão sendo marcados.
  4. Ao terminar: SUCESSO (verde) ou ERRO (vermelho, com o step e a mensagem — consultar a matriz de recovery abaixo). O botão fica desabilitado enquanto houver um reset EM_ANDAMENTO (só um por vez).

Com a estratégia coolify ativa, o PowerSync reinicia sozinho ao final.

Opção B — Endpoint REST

Sem COOLIFY_API_TOKEN (restart manual): parar o PowerSync ANTES. Sem isso, o slot fica ativo e o RESET_SLOT falha (replication slot ... is active) — a falha mais comum. Subir de volta ao fim (step 3). Com coolify, o PAUSE/RESUME é automático e este passo é dispensável.

BASE="https://excel.onpetro.xadm.biz"
curl -X POST "$BASE/api/comercial/admin/nuke-replication" \
     -H "Authorization: Bearer <BI_COMERCIAL_XLS_API_TOKEN>" \
     -H "X-Confirm-Nuke: <POWERSYNC_NUKE_CONFIRMATION>" \
     -H "X-Operator-Id: $USER" \
     -H "Content-Type: application/json" \
     -d '{"motivo": "limpeza trimestral 2026Q2"}'
# → 202 { "nukeId": 42, "status": "EM_ANDAMENTO", "linkStatus": ".../nuke-replication/42" }

Polling do status:

curl "$BASE/api/comercial/admin/nuke-replication/42" \
     -H "Authorization: Bearer <BI_COMERCIAL_XLS_API_TOKEN>"

Acompanhar status (EM_ANDAMENTO → SUCESSO) e stepsCompletados. Tempo típico: < 60 s + POWERSYNC_BOOTSTRAP_WAIT.

Subir o PowerSync de volta — só com restart manual:

docker start powersync                         # ou
kubectl scale deployment/powersync --replicas=1

Verificação

  • Telegram de ops: ✅ NUKE CONCLUÍDO em N ms.
  • Smoke em 1 cliente mobile: reabrir o app e confirmar o re-sync (download dos buckets atuais; o bootstrap escala com o volume de bi_*).
  • O sinal ultimo_nuke em bi_configuracao é atualizado no FINALIZAR — o cliente Dart compara com o valor local, faz disconnectAndClear() e re-sincroniza do zero (decisão 0014; etapa 07).

Reversão / recovery por step

Sem rollback automático. Falha → status ERRO + Telegram ❌ NUKE FALHOU em step X. A única ação depois da falha é o RESUME quando ela acontece em DROP_MONGO (o slot já é novo). Resets órfãos em EM_ANDAMENTO (container morreu no meio) são marcados ERRO automaticamente pelo NukeStartupRecovery no próximo boot ("Interrompido após reinício da aplicação"; step_atual preservado para debug).

Step Estado deixado pelo erro Recovery
VALIDAR/LOCK outro reset em andamento (409 já no POST) aguardar o concorrente terminar e re-chamar
PAUSE none: no-op. coolify: API não respondeu / UUID não resolveu — nada destrutivo tocado sem cleanup; corrigir config/allowlist; re-chamar
SNAPSHOT_PRE PG consultado, Mongo intacto sem cleanup; re-chamar
RESET_SLOT slot dropado mas não recriado psql: SELECT * FROM pg_replication_slots WHERE slot_name='powersync'; — se vazio, SELECT pg_create_logical_replication_slot('powersync','pgoutput'); e subir o PowerSync
DROP_MONGO Mongo parcial/intacto (pior caso); o nuke já tentou o RESUME mongosh "$URI": use <db>; db.dropDatabase(); reiniciar o PowerSync (bootstrap fresh). O <db> vem do path da POWERSYNC_MONGODB_URI
RESUME destrutivo já feito, PowerSync parado coolify: reiniciar o recurso no Coolify. none: docker start powersync / kubectl scale --replicas=1
SAMPLE_POST/FINALIZAR reset de fato OK, só a finalização falhou SQL de emergência abaixo

SQL de emergência (finalização falhou — fecha o reset e grava o sinal pros clientes):

-- 1. marca o reset como concluído
UPDATE xls_nuke_replication SET status = 'SUCESSO' WHERE id = N;

-- 2. grava o sinal pros clientes, se o STEP_FINALIZAR não gravou.
--    'ultimo_nuke' é contrato (lido pelo cliente Dart) — não renomear.
INSERT INTO bi_configuracao (id, valor, tipo, sistema, atualizado_em)
VALUES ('ultimo_nuke', NOW()::TEXT, 'TIMESTAMP', TRUE, NOW())
ON CONFLICT (id) DO UPDATE SET valor = EXCLUDED.valor, atualizado_em = NOW();

Falha mais provável — RESET_SLOT por slot ativo: sintoma replication slot ... is active; causa = PowerSync não parou antes. Diagnóstico: SELECT slot_name, active, active_pid FROM pg_replication_slots;. Preferir parar o PowerSync e re-chamar (a estratégia coolify faz PAUSE/RESUME sozinha). Último recurso: kill -9 <active_pid>.

Sintomas pós-reset

Sintoma Causa provável Ação
Cliente mobile não re-sincroniza ao reabrir PowerSync não voltou a rodar docker ps / kubectl get pods; subir o recurso
Bootstrap demorando (> 10 min) volume grande em bi_* ou conexão fraca esperar; checar logs do PowerSync e o storage do Mongo crescendo
Storage Mongo voltou a crescer rápido upsert diff-aware não está filtrando as linhas inalteradas verificar se xls_processamento.linhas_efetivas está sendo gravado
Erros de checkpoint nos clientes slot novo à frente do esperado esperar o bootstrap; se persistir, rodar o reset de novo

Requests prontos (IntelliJ HTTP Client / VS Code REST Client)

Cole num arquivo .http local e preencha os segredos com os valores do deploy (nunca commitar os tokens reais — use variáveis de ambiente/secrets do seu cliente HTTP). Rode em ordem: POST → GET do status, repetindo o GET até SUCESSO/ERRO.

@base_url     = https://excel.onpetro.xadm.biz
@bearer_token = <BI_COMERCIAL_XLS_API_TOKEN>
@nuke_token   = <POWERSYNC_NUKE_CONFIRMATION>
@operator_id  = seu-usuario

### Dispara o reset — captura o nukeId da resposta
POST {{base_url}}/api/comercial/admin/nuke-replication
Authorization: Bearer {{bearer_token}}
X-Confirm-Nuke: {{nuke_token}}
X-Operator-Id: {{operator_id}}
Content-Type: application/json

{ "motivo": "limpeza trimestral" }

### Polling do status — repetir até status = SUCESSO ou ERRO (< 60s + bootstrap-wait)
GET {{base_url}}/api/comercial/admin/nuke-replication/{{nuke_id}}
Authorization: Bearer {{bearer_token}}

### Override do bootstrap-wait (útil em janela de teste curta)
POST {{base_url}}/api/comercial/admin/nuke-replication?wait_secs=10
Authorization: Bearer {{bearer_token}}
X-Confirm-Nuke: {{nuke_token}}
X-Operator-Id: {{operator_id}}
Content-Type: application/json

{ "motivo": "smoke test" }

### Listagem paginada (mais recentes primeiro; page base 0, size default 20 e máximo 100)
GET {{base_url}}/api/comercial/admin/nuke-replication?page=0&size=20
Authorization: Bearer {{bearer_token}}

A listagem responde o Page do micronaut-data (forma): execuções em content, cada uma na forma do polling de status; total em totalSize; página e tamanho em pageable.number/pageable.size.

Smoke checks de segurança (validam o deploy): POST sem Authorization → 401; sem X-Confirm-Nuke ou com token errado → 403; GET .../nuke-replication/999999999 → 404.