1. Três confirmações diferentes
Um job gravado, uma mensagem aceite e uma projeção atualizada são três factos diferentes. No laboratório, três ficheiros SQLite representam produtor, fila e consumidor. O produtor confirma o job e a intenção na outbox; o relay transfere essa intenção; o consumidor aplica uma projeção e guarda a identidade processada. Nenhuma transação atravessa os três ficheiros. Este desenho torna visíveis as fronteiras que um diagrama com uma única seta pode esconder. Num incidente, começa por localizar a última confirmação comprovada, em vez de declarar sucesso apenas porque o primeiro serviço respondeu.
2. Interromper antes e depois do commit
O runner inicia um processo filho que abre BEGIN IMMEDIATE e insere job-a. No primeiro caso, termina com os._exit(73) antes da outbox; no segundo, termina depois da outbox mas antes de COMMIT. Ao reabrir a base, ambos mostram zero jobs e zero eventos. No terceiro, a saída ocorre após COMMIT e ambos permanecem. O código de saída é igual, mas o estado é diferente. A evidência relevante inclui o ponto de interrupção e as leituras posteriores. O ensaio usa journal DELETE e synchronous FULL; não simula perda de energia nem valida todos os comportamentos de um disco.
3. Publicar e marcar são operações separadas
O relay lê um evento pendente, grava a entrega na fila local e só depois marca a outbox como publicada. Uma exceção entre as duas gravações deixa a entrega aceite e a intenção ainda pendente. Após reinício lógico do relay, a publicação repete-se: o ensaio encontra duas entregas com a mesma identidade. Inverter a ordem não elimina o problema; marcar primeiro pode ocultar um evento nunca entregue. O consumidor precisa de tolerar repetição, e a operação precisa de monitorizar intenções presas. A fila SQLite é um fixture original, sem as garantias ou mecanismos de reconhecimento de um broker comercial.
4. Definir identidade e âmbito do consumidor
CloudEvents 1.0.2 combina source e id para identificar um evento. Uma tentativa de transporte pode mudar sem representar nova ocorrência. Quando o mesmo evento alimenta efeitos independentes, a inbox precisa de refletir também o consumidor. No exemplo, projection-v1 e projection-v2 têm linhas distintas para a mesma source/id, porque cada uma constrói a sua projeção. O fixture compara ainda um fingerprint do envelope: a mesma identidade com payload diferente é um conflito, não uma correção silenciosa. Este controlo é uma escolha do laboratório. Não demonstra conformidade integral com CloudEvents nem autentica quem emitiu o evento.
5. A marca de conclusão acompanha o efeito
O consumidor abre uma transação local, verifica a inbox, insere a marca e atualiza a projeção. Uma exceção depois da marca faz rollback de ambas; a tentativa seguinte continua possível. Após um commit bem-sucedido, a repetição devolve duplicate e conserva total=7. O ensaio mostra uma aplicação local perante duas entregas. Se acrescentares uma chamada externa, essa chamada não passa a pertencer à transação SQLite. Uma queda depois do efeito remoto pode exigir consulta, repetição idempotente ou compensação. Define esse comportamento antes de promover a solução para uma integração que envia notificações ou altera outro sistema.
6. Exercício de diagnóstico por fronteira
Prepara uma tabela para um job afetado com confirmação do produtor, presença na outbox, evidência de entrega, marca do consumidor e valor da projeção. Para cada coluna, indica a origem da evidência e o que permanece desconhecido. No caso de marcação antecipada pelo relay, uma outbox sem pendentes não prova conclusão; reconcilia as referências afetadas antes de reativar envios. O trabalho de APS inclui controlar o âmbito da recuperação, manter registos originais e obter autorização para reparar dados. O laboratório serve para ensaiar hipóteses concretas, não para recomendar acesso direto a bases de produção.
BEGIN IMMEDIATE;
INSERT INTO jobs(id,state) VALUES ('job-a','accepted');
INSERT INTO outbox(id,payload,created) VALUES ('evt-1','{}',10);
COMMIT;
-- Teaching outline: publication happens later, outside this transaction.Saída 73 antes de COMMIT: jobs=0/outbox=0. Saída 73 depois de COMMIT: jobs=1/outbox=1. O relay pode criar duas entregas, mas a inbox preserva uma aplicação local.
Armadilhas comuns
Marcar publicado antes de enviar; regenerar identidade no retry; deduplicar só por id; confirmar inbox separadamente do efeito.
Tópicos relacionados: Transações e persistência · Eventos e identidade · Recuperação operacional
Cada confirmação tem um âmbito. Preserva identidade e grava a marca de processamento com o efeito que ela comprova.
Referência: Transactional outbox pattern · Microservice architecture patterns and scoped platform examples; primary guidance consulted 2026-09-30