Pular para conteúdo

Reset — recomeçar a integração do zero (Maxsul)

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

O que é

Procedimento para zerar a integração PIED → X-Adm e recomeçar limpo, sem inundar o X-Adm com inserts/updates. Serve quando uma mudança de identidade canônica ou de contrato exige recomeçar a base — ex.: a adoção do cliente do Faturamento (decisão 0009), que entra sem backfill nem re-envio dos pedidos existentes.

O desenho (arquitetura, fluxo) é do livro do projeto; como subir e desligar no dia a dia é de Implantação. Aqui está só o reset.

Por que não é só "apagar as tabelas"

O PowerSync sincroniza as 5 tabelas espelho (contratos, propriedades, fones, itensped, estoque) para o cliente do X-Adm. Um resync completo re-snapshota a fonte: se a fonte ainda tiver linhas velhas quando o PowerSync subir, elas viram um flood de escrita no X-Adm. Também há o lixo do espelho antigo — propriedade/estoque/fone sem contrato no X-Adm não vem do maxsul-pied (que só envia por pedido, no PushService.enviarPedido), e sim de um snapshot anterior que não foi esvaziado.

Chave anti-flood: esvaziar a fonte ANTES de o PowerSync subir fresh, com o cliente do X-Adm parado. Fonte vazia + cliente parado = zero flood.

Pré-requisitos

  • Acesso ao Coolify da Maxsul (recursos pied.maxsul, integrador.maxsul, powersync.maxsul + o Postgres compartilhado).
  • Acesso ao repo do PowerSync (maxsul/powersync) e ao cliente do X-Adm (integrador-client).
  • Secrets no cofre da X-Adm (nunca neste doc): PS_*, MONGO_ROOT_*, credenciais do Postgres.
  • Backup antes de apagar (pg_dump do banco do cliente).

Topologia (o que existe)

  • 1 Postgres compartilhado, 1 banco por cliente. O do Maxsul contém pied_* e as tabelas espelho do Integrador (schema public), com dois históricos Flyway (o do pied e o do Integrador).
  • PowerSync (Maxsul): storage em MongoDB (dois volumes: estado do bucket + metadados). A config está assada na imagem — matar o disco do Mongo não perde config. Lê da fonte via replication slot + publication no Postgres (o slot vive no Postgres, não no Mongo). Sincroniza só as 5 espelho.
  • Cliente do X-Adm = integrador-client: consome o PowerSync e escreve no ZIM em lotes.

Passo a passo

Ordem importa — cada passo evita um modo de flood do seguinte.

  1. Parar quem escreve. Pare o pied.maxsul e o integrador.maxsul (para o push por pedido) e pare o cliente integrador-client (para a escrita no ZIM).
  2. Backup. pg_dump do banco do cliente (fonte da verdade antes de apagar).
  3. Parar o PowerSync (powersync.maxsul).
  4. Esvaziar a fonte com DELETE, nunca TRUNCATE (por quê no fim deste doc): as 5 tabelas espelho + as pied_*. DELETE, não DROP TABLE — o drop quebra a publication e o Flyway do Integrador sem ganho. ⚠️ Reseede os singletons logo em seguida (abaixo): há tabela pied_* cuja linha única é semeada pela migration, e o DELETE a leva junto.
  5. Matar o storage do PowerSync (docker compose down -v, ou remover só os volumes do Mongo se o Coolify já removeu o container no Stop). Não exclua o recurso Coolify — isso perde os secrets PS_*/MONGO_ROOT_*.
  6. Dropar o replication slot órfão no Postgres, de forma idempotente — só os inativos:
    SELECT pg_drop_replication_slot(slot_name)
    FROM pg_replication_slots
    WHERE database = '<banco_do_cliente>' AND NOT active;
    
  7. Subir apps + PowerSync fresh. Suba pied.maxsul/integrador.maxsul e redeploy do powersync.maxsul (Mongo vazio → snapshot de uma fonte vazia, sem flood).
  8. Semear o cursor da PIED para não re-puxar o histórico: pied_cursor com a data de hoje. E reseedar os singletons apagados no passo 4 (ver abaixo).
  9. Zerar o cliente do X-Adm. disconnectAndClear no integrador-client (limpa o SQLite local) e religue.

Singletons semeados por migration (reseedar depois do DELETE)

Nem toda tabela pied_* é só dado: algumas têm uma linha fixa semeada pela migration, e o código a atualiza com UPDATE ... WHERE id = 1. Se o DELETE do passo 4 leva a linha e ninguém reseeda, o UPDATE afeta 0 linhas — falha silenciosa, sem erro no log.

-- pied_integracao_estado (V4): corte go-forward. Reseedar após o DELETE das pied_*.
INSERT INTO pied_integracao_estado (id) VALUES (1) ON CONFLICT (id) DO NOTHING;

Hoje é inerte, não deixe enganar. Desde a revisão do gate (2026-08-07) quem manda no envio é a transição de pagamento no upsert; o corte go-forward ficou sem chamador, e o código que o gravava saiu na migração para micronaut-data (2026-09-11). A tabela ficou — só o GET /dados/estado a lê; dropá-la é DDL destrutivo, decisão à parte (expand/contract). A linha ausente não trava envio nenhum — mas deixa GET /dados/estado vazio, o que atrapalha o diagnóstico (já custou uma investigação, 2026-09-04). Reseede.

Ao criar um singleton novo (INSERT ... VALUES (1) numa migration), acrescente-o aqui no mesmo PR — senão o próximo reset o apaga em silêncio.

Variante: reset só-envio (mantém o raw)

Usada na ida pra produção (2026-09-04): recomeçar o envio do zero sem perder a captura raw já feita (evita re-puxar meses de PIED). Zera o lado X-Adm e a fila, mantém pied_webhook, pied_rest, pied_cliente, pied_produto e o payload do pied_pedido. É o reset total menos o DELETE das pied_* — no lugar dele, reseta a máquina de entrega.

Mesma ordem e mesmos cuidados do passo a passo acima (parar escritores → backup → parar PowerSync → mexer no Postgres → matar Mongo → dropar slot → subir fresh → disconnectAndClear). Só o passo 4 muda, e o passo 8 (semear cursor) não se aplica — mantendo o raw, o cursor continua de onde parou (reseedar arriscaria pular pedidos atualizados no intervalo).

Passo 4 (variante) — DELETE nunca TRUNCATE:

BEGIN;
-- (a) Espelho X-Adm: esvazia a FONTE antes do PowerSync subir fresh (anti-flood).
DELETE FROM estoque;  DELETE FROM itensped;  DELETE FROM fones;
DELETE FROM propriedades;  DELETE FROM contratos;

-- (b) Outbox M2M: mata o dispatch pendente/enviado do push. 'xadm-push' é o ÚNICO tipo do PIED
--     (PushEnviador.TIPO); o write-back do X-Adm volta na resposta HTTP, NÃO cria linha ENTRADA.
DELETE FROM mensagem_m2m WHERE tipo = 'xadm-push';

-- (c) Reset da máquina de entrega no pied_pedido — MANTÉM linha e payload (raw).
--     payment_status_anterior := payment_status => já-pago NÃO refira o gate (sem transição
--     testemunhada), senão o backlog inteiro despejaria no X-Adm.
UPDATE pied_pedido SET
    status                  = 'CAPTURADO',
    content_hash            = NULL,
    cod_retorno             = NULL,
    msg_retorno             = NULL,
    chave_xadm              = NULL,
    enviado_em              = NULL,
    payment_status_anterior = payment_status,
    atualizado_em           = now();
COMMIT;

O gate manda no "só pedidos novos". O disparo do envio é a transição payment_status não-received → received num pedido CAPTURADO (upsert ON CONFLICT, V6__gate_pagamento.sql + PiedPedidoRepository.upsert). Ao setar payment_status_anterior = payment_status, os já-received não refira; só quem pagar depois do reset entra na fila. Pedido que reaparece por webhook é INSERT (1ª captura) — já-pago no INSERT nunca dispara.

Opção — começar do zero de verdade (usada em 2026-09-04): os já-pagos de teste eram ruído; apagamos em vez de deixar parados em CAPTURADO. Mantém só os pendentes reais, que vão pro X-Adm quando pagarem:

DELETE FROM pied_pedido WHERE payment_status = 'received';

Verificação (antes de ligar a Fase 2):

SELECT status, count(*) FROM pied_pedido GROUP BY status;   -- só CAPTURADO
SELECT count(*) FROM mensagem_m2m WHERE tipo='xadm-push';   -- 0
SELECT count(*) FROM contratos;                             -- 0 (idem as outras 4 espelho)
SELECT count(*) FROM pied_webhook;                          -- >0 (raw intacto)

Por que DELETE e não TRUNCATE

Este banco tem PowerSync com replicação lógica; TRUNCATE não se comporta bem com a publication usada pelo slot. Use DELETE nas tabelas espelho e pied_*. (Regra do repo — nunca TRUNCATE onde há PowerSync.)

Depois do reset

  • Confira que o X-Adm não recebeu escrita durante o procedimento (cliente parado).
  • Religue a Fase 2 conforme Implantação só quando a fonte estiver populada de novo pelos pedidos correntes.
  • Runbooks operacionais do PowerSync em si (Mongo, slot, docker) vivem nos repos donos (maxsul/powersync e integrador-server); aqui fica só a fatia do maxsul-pied.