Sync, logs, checkpoints e replay
Imagine uma loja sem internet por oito horas. O operador continua emitindo orçamentos. Quando a rede volta, o runtime precisa saber o que já foi enviado, o que é novo, o que é retry e o que é conflito.
O Runtime precisa responder:
- quais registros foram enviados antes da queda;
- quais registros são novos;
- se um reenvio é retry ou payload conflitante;
- se o batch anterior aceito combina com a chain do sender;
- de onde o replay deve continuar após um crash.
Sync no AppCore é replicação conservadora leader-to-follower. Não é RAFT, multi-master nem resolvedor de conflitos de domínio.
O que acontece quando a loja reconecta?
Commands locais podem continuar conforme a política de storage e command. Quando a rede volta, o receiver não confia no batch: valida identidade, protocolo, sequência, count, tamanho, hash, previous hash e checkpoint.
O receiver verifica:
- compatibilidade da identidade de origem;
- intervalo de sequence;
- quantidade declarada de eventos;
- tamanho do payload;
- SHA-256 dos eventos;
- hash do batch anterior;
- sequences repetidas;
- estado do checkpoint.
Se a mesma sequência reaparece com o mesmo payload, é retry. Se a mesma sequência reaparece com bytes diferentes, não é retry: é conflito.
O que um SyncMessage prova?
Um batch carrega batch_id, source node, sequence_start, sequence_end, event count, hash dos eventos, timestamp, previous batch hash e payloads opacos.
batch_idcomo identidade idempotente;- source node ID;
sequence_startesequence_endinclusivos;- quantidade declarada de eventos;
- hash de metadata e payloads prefixados por tamanho;
- creation time;
- hash opcional do batch anterior;
- payloads opacos dos eventos.
O hash cobre metadata e tamanho dos payloads, não apenas os bytes soltos. Isso impede aceitar o mesmo payload com metadata de sequência diferente.
O batch comprova consistência da replicação no nível do transport. Ele não prova que o evento de negócio está semanticamente correto. O Runtime protege ordem e integridade; a aplicação continua dona do significado do domínio.
Por que manter replication log?
O log file-backed usa marcador # appcore-replication-log-v1, limite total, limite por record, sequence map, hash chain, lock e atomic write. Append por sequência é idempotente: mesma sequência e mesmo payload retorna o índice original; mesma sequência e payload diferente é conflito.
Ele:
- usa o marcador estável
# appcore-replication-log-v1; - limita os bytes totais;
- limita os bytes de cada record;
- armazena sequence e metadata da hash chain;
- recarrega e valida records antes de append;
- usa process locks e writes atômicos;
- recupera um prefixo válido quando a tail é interrompida.
O log é evidência de replay. Sem log, recovery teria que confiar em projections de aplicação, que podem ter sido compactadas, migradas ou parcialmente reconstruídas.
Por que checkpoint existe se já existe replay?
Checkpoint guarda última sequência aceita e batch hash por peer em formato # appcore-sync-checkpoint-v1. Peer IDs e hashes são validados e o arquivo é substituído atomicamente.
# appcore-sync-checkpoint-v1
peer-a=42,2f4c...
peer-b=17,
Sem checkpoint, recovery teria que replayar tudo ou inferir progresso pela projection. AppCore não faz essa inferência.
Replay sozinho não basta porque o log pode ser maior que o ponto útil de recovery e projections nem sempre são autoritativas. O checkpoint é uma promessa explícita de que os batches até aquela sequence e hash foram aceitos.
Como a outbox durável faz recovery?
A outbox file-backed do 1.0.2-rc usa o marcador binário explícito
appcore-sync-outbox-v2. Cada enqueue ou ACK acrescenta e sincroniza um frame
limitado. Ordinais, comprimentos no início/fim e uma cadeia SHA-256 detectam
corrupção, duplicação e reordenação. Somente um frame final incompleto é
truncado após crash; um frame completo inválido falha fechado.
Espaço confirmado é recuperado por compactação atômica, que grava apenas mensagens pendentes e muda a geração. Outro processo com visão antiga detecta a mudança e recarrega. O journal continua limitado a 64 MiB e reserva tail suficiente para confirmar a mensagem frontal já aceita.
O contrato de outbox do 1.0.2-rc lê um prefixo ordenado com limites
independentes de quantidade e bytes codificados antes de clonar payloads. Ele
expõe stats sem payload, persiste attempts/readiness de retry e aplica somente
receipts de prefixo ordenado exato. O follower e a CLI do Runtime usam esse
caminho diretamente, então o crescimento da fila não define uma única alocação
de entrega nem o avanço do checkpoint.
Essa é uma fronteira explícita de formato persistente. Arquivos V1, sem versão
ou futuros retornam NO MORE SUPPORTED PLEASE UPDATE; o Runtime não infere nem
converte. Operadores drenam V1 antes do upgrade e V2 antes do rollback.
O que torna o replay seguro?
Idempotency de command e idempotency de batch resolvem problemas diferentes. A key de command evita duplicar retry de cliente. A sequência/checkpoint evita duplicar replicação entre peers. Os dois limites precisam existir porque os retries acontecem em fronteiras diferentes.
Persistência SQLite opcional depois do 1.0
A prévia pós-1.0 publicada appcore-sync-sqlite 0.1.0-alpha.6 persiste apenas registros de sync do
Runtime: replication log, outbox, checkpoints por peer e tombstones opacos. Ela
usa schema interno versionado, WAL, sincronização completa, conexões e limites
explícitos, snapshots portáveis, backup online verificado e integrity scan que
falha fechado. Ela nunca expõe SQL arbitrário nem tabelas de aplicação.
O provider suporta processos locais independentes em filesystem com locking
confiável. Shares de rede, SQLite multi-host e seleção automática pelo manifest
V1 estável estão fora do contrato. Schemas internos desconhecidos ou futuros
retornam NO MORE SUPPORTED PLEASE UPDATE; formatos file-provider não são
importados por inferência. Veja a
prévia appcore-sync-sqlite.
Por que AppCore não resolve conflitos automaticamente?
Replicação multi-master exige um modelo de conflito do domínio. O Runtime não sabe se reservar estoque, editar uma nota, aprovar um orçamento e rotacionar um secret possuem a mesma semântica. Por isso sync permanece conservador e a aplicação possui a policy de conflito de negócio.
Limitações
- Sync é leader-to-follower, não RAFT nem multi-master.
- AppCore detecta conflito de sequência/hash, mas não mescla alterações de negócio.
- Checkpoint prova progresso aceito pelo runtime, não correção de projection.
- Replay depende de handlers respeitarem idempotency.
- Outboxes file V1 e V2 não são mutuamente legíveis; upgrade e rollback exigem fila durável vazia.
- Partições de rede são tratadas de forma conservadora; writes que exigem liderança não prometem disponibilidade global contínua.
Próximo: operação distribuída.