A autorização pode envelhecer
Um worker fictício valida que pode executar um job e fica suspenso antes de escrever. Entretanto, outro worker recebe a nova autorização. Quando o primeiro regressa, a sua memória continua a conter uma decisão que já não corresponde ao estado atual. O problema não desaparece por reduzir o intervalo entre consulta e escrita. O destino que aceita o efeito tem de conseguir distinguir o pedido antigo. O artigo Chubby descreve o uso de informação de geração em pedidos protegidos; esta aula usa um contrato próprio e simplificado, sem implementar Chubby, emissão distribuída ou um produto de fencing.
Contrato explícito por recurso
O laboratório cria dois recursos sintéticos, fund-A e fund-B, cada um com uma geração instalada e um valor inteiro. Uma função que representa autoridade confiável instala uma geração superior. O worker apresenta recurso, geração, identificador de pedido e delta. O destino exige igualdade com a geração instalada: um número maior inventado pelo worker também é rejeitado. Esta escolha é específica do exercício e não uma regra universal de fencing tokens. A origem confiável das gerações é uma premissa, não uma funcionalidade implementada. Numa integração real, acesso à instalação e associação da autorização ao chamador precisam de proteção demonstrável.
Aplicar a decisão na transação
Duas ligações SQLite acedem ao mesmo ficheiro temporário. A função apply inicia BEGIN IMMEDIATE antes de ler a geração, verificar o pedido e alterar o inteiro. Assim, a instalação de outra geração e a escrita não passam entre a validação e o efeito dentro dessa transação local. O ensaio mostra uma segunda ligação rejeitada por contenção enquanto a primeira mantém a transação aberta; após libertação, volta a progredir. O erro de lock e a rejeição de geração são causas diferentes. O harness usa transações SQL explícitas e timeout zero para tornar esta observação reproduzível, sem apresentar esse valor como configuração recomendada de produção.
Autoridade não elimina duplicações
Uma geração válida pode enviar duas vezes a mesma intenção. Por isso, o modelo mantém um registo separado por recurso e identificador de pedido. A primeira chamada aplica o delta e guarda o resultado na mesma transação. Uma repetição com a mesma intenção devolve o resultado guardado; uma alteração do delta com o mesmo identificador é rejeitada. Se r1 devolveu 7 e r2 levou o valor a 12, repetir r1 devolve 7 sem reduzir o estado atual. A resposta pertence ao pedido original. Para conhecer o estado presente, realiza a leitura adequada em vez de reinterpretar a resposta de replay.
Contraexemplos que delimitam a proteção
O laboratório injeta uma exceção entre atualizar o inteiro e guardar o pedido: ROLLBACK preserva o valor anterior e não regista o pedido abortado. Isto cobre apenas efeitos na mesma base local. Uma chamada API externa não seria desfeita por esse rollback. Depois, o exercício escreve diretamente por SQL e contorna deliberadamente a função protegida. Também simula uma má recuperação que repõe a geração antiga; um pedido dessa geração volta a ser aceite. Estes contraexemplos mostram por que razão todos os caminhos de escrita, o estado restaurado e a origem das autorizações fazem parte da garantia. Reabrir o ficheiro não é um ensaio de crash nem de perda de energia.
Oficina: rever um desenho de APS
Usa quarenta e cinco minutos em pares. Nos primeiros quinze, desenha um scheduler fictício com dois workers, o emissor de gerações e o destino. Marca todos os caminhos capazes de produzir efeitos. Nos quinze seguintes, acrescenta uma pausa longa, uma resposta perdida e um acesso SQL direto. Explica em inglês que controlo trata cada falha e qual permanece sem cobertura. Nos quinze finais, apresenta ao gestor uma recomendação com evidência, limite e responsável. Se o destino não puder aplicar o contrato, identifica o mecanismo de isolamento suportado que terá de ser ensaiado. Não declares isolamento físico apenas porque o modelo local passou.
python3 content/labs/ha-fencing/run.py
# Python 3.13; standard library only.
# Creates and deletes its own temporary SQLite database.
# No network, existing database or physical fencing operation.A lê geração 1; o destino instala 2; apply(A,1) é rejeitado. Uma escrita SQL direta contorna o contrato e demonstra a necessidade de cobrir todos os caminhos.
Armadilhas comuns
Confiar numa consulta anterior; aceitar números inventados; usar geração como deduplicação; permitir caminhos diretos; restaurar autorizações antigas; assumir rollback de uma API externa.
Tópicos relacionados: Domínios de falha e capacidade residual · Quorum e isolamento de escritores · Substituição, identidade e aceitação
A garantia depende de quem emite a autorização, de onde o efeito é admitido e de como o estado e a identidade do pedido sobrevivem à recuperação.
Referência: The Chubby lock service for loosely-coupled distributed systems · DR HA 2026-09; Pacemaker 3.0, etcd 3.6, PostgreSQL 18 and selected Kubernetes/AWS behavior