Ir para o conteúdo

Projeto independente de P&D · Colônia

Rastreabilidade de ponta a ponta

Uma única identidade rastreável liga as etapas relevantes, da entrada até a saída resultante.

Não normativo

Versão do Companion
1.0
Corresponde ao COADF Core
2.2
Status
Atual
Última revisão
Princípios do COADF
P-4

Propriedade de arquitetura

O COADF P-4 é publicado na íntegra. Um identificador é criado quando um documento dá entrada e viaja com cada etapa de processamento, cada avaliação de confiança, cada decisão humana e a saída publicada. A trilha é somente de inserção, uma consulta reconstrói a cadeia, e a cadeia se exporta em JSON para quem não construiu o sistema. O esquema publicado da trilha de auditoria nomeia os campos: trace_id, timestamp, event_type, actor, input, output, decision, source_hash e immutable.

Duas propriedades a sustentam. Continuidade: cada etapa carrega a mesma identidade, em cada fronteira que atravessa. Persistência: o registro de cada etapa sobrevive, e ninguém consegue editá-lo depois para que a resposta pareça melhor.

Por que importa

A pergunta a que o P-4 responde chega tarde e de fora. Meses depois da publicação, alguém pergunta de onde veio um valor, o que o leu e quem olhou para ele. A resposta precisa poder ser reconstruída por uma pessoa que não construiu o sistema, a partir de registros que ninguém poderia ter reescrito nesse meio-tempo.

Um trace distribuído não é uma trilha de auditoria de negócio

Esta é a distinção que a maioria das implementações borra, porque as ferramentas se parecem: as duas têm um identificador, as duas atravessam serviços, as duas mostram etapas em sequência. Elas respondem a perguntas diferentes e fazem promessas diferentes.

Trace distribuído e trilha de auditoria, comparados
AspectoTrace distribuídoTrilha de auditoria
FinalidadeExplicar uma execução: latência, erros, estrutura de chamadasEstabelecer o que aconteceu com uma transação, e quem agiu
IdentidadeUm trace por requisição ou job, carregado no cabeçalho W3C traceparentUm trace_id por transação de negócio, gerado na entrada
Tempo de vidaA duração da requisição; retenção medida em diasTanto quanto os registros que ela justifica
AmostragemA amostragem é normal e permitidaSem amostragem: uma entrada ausente é um defeito
MutabilidadePipelines filtram, descartam e reescrevem por configuraçãoSomente de inserção: uma correção é uma nova entrada
ConteúdoSpans, tempos, atributos escolhidos para diagnósticoEventos de negócio: entrada, saída, decisão, ator

A especificação W3C Trace Context define o cabeçalho traceparent, e os SDKs do OpenTelemetry propagam tracecontext e baggage por padrão. Essa identidade é por execução. No OpenTelemetry, um trace que não é amostrado não é exportado, e o filter processor do Collector descarta a telemetria que corresponde a uma condição. Os dois são comportamento correto para diagnóstico e desqualificante para evidência.

Os dois se correlacionam: o trace_id de auditoria é gravado em cada span como atributo, e o identificador do trace distribuído, nos logs. Nenhum dos dois deve virar o outro. Uma única transação de negócio costuma abranger vários traces distribuídos: uma requisição de entrada, um job de processamento assíncrono, uma decisão tomada dias depois, a saída.

A correlação copia o identificador de auditoria para a telemetria, e a telemetria atravessa outras fronteiras: exportadores, fornecedores, regras de acesso e retenção normalmente mais frouxas que as do armazenamento de auditoria. O identificador deve ser classificado antes de sair: uma referência opaca, nunca dado pessoal nem segredo. Onde o identificador de auditoria carrega significado próprio, a correlação passa por uma referência separada e não sensível.

Arquitetura de exemplo · Não normativa

Traces distribuídos e uma trilha de auditoria

Exemplo de arquitetura, não normativo: quatro traces distribuídos separados, acima, um por requisição ou job, e abaixo deles uma trilha de auditoria cujas entradas ilustrativas compartilham um único trace_id.Faixa superior, traces distribuídos: quatro traces separados, um para uma requisição de recepção, um para um job de processamento, um para uma decisão tomada dias depois e um para uma saída. Eles não estão ligados entre si. Faixa inferior, a trilha de auditoria: quatro entradas ilustrativas, recepção, processamento, decisão e saída, todas com o mesmo trace_id, persistidas separadamente da telemetria e não amostradas. Uma nota na faixa diz que os eventos são ilustrativos, não um fluxo de trabalho exigido pelo COADF. Uma linha tracejada liga cada trace à entrada que lhe corresponde, rotulada como um atributo de span que carrega o trace_id de auditoria.Traces distribuídos: contexto de execução, pode ser amostrado, vida curtaTrilha de auditoria: um trace_id, somente de inserção, não amostradaTrace Arequisição de recepçãoTrace Bjob de processamentoTrace Cdecisão, dias depoisTrace DsaídarecepçãoprocessamentodecisãosaídaEventos ilustrativos, não um fluxo de trabalho exigido pelo COADF; o mesmo trace_id em cada entradaatributo de span
  • Correlação pelo trace_id de auditoria
Uma transação de negócio, quatro traces distribuídos. A telemetria pode ser amostrada e tem vida curta; as entradas de auditoria são persistidas separadamente e não são amostradas. Elas se correlacionam pelo trace_id de auditoria, registrado em cada trace como atributo de span. Os eventos são ilustrativos, não um fluxo de trabalho exigido pelo COADF.

Descrição textual. Faixa superior, traces distribuídos: quatro traces separados, um para uma requisição de recepção, um para um job de processamento, um para uma decisão tomada dias depois e um para uma saída. Eles não estão ligados entre si. Faixa inferior, a trilha de auditoria: quatro entradas ilustrativas, recepção, processamento, decisão e saída, todas com o mesmo trace_id, persistidas separadamente da telemetria e não amostradas. Uma nota na faixa diz que os eventos são ilustrativos, não um fluxo de trabalho exigido pelo COADF. Uma linha tracejada liga cada trace à entrada que lhe corresponde, rotulada como um atributo de span que carrega o trace_id de auditoria.

Estratégias de implementação válidas

Gerar uma vez, na entrada

O identificador é criado onde a transação entra e em nenhum outro lugar. Todo componente posterior o exige e nenhum gera um. Um identificador ausente é um erro, nunca um motivo para criar um novo, e um valor padrão num modelo de mensagem é a forma mais comum de quebrar essa regra sem que ninguém perceba.

Carregá-lo explicitamente em cada fronteira

  • No processo: um argumento de função ou o contexto da requisição.
  • Entre serviços: um campo no contrato da requisição ou da mensagem, onde um esquema pode exigi-lo.
  • Em trabalho agendado: um argumento do job.
  • Em repouso: uma coluna em cada linha armazenada.

Bibliotecas de propagação carregam o contexto de execução por conta própria. A identidade de auditoria faz parte do contrato da aplicação e pertence ao payload.

Persistir somente por inserção

Várias realizações são válidas, e elas se combinam:

  • Persistência somente de inserção. O repositório oferece inserir e ler, e nada mais.
  • Privilégios do banco de dados. O papel da aplicação tem INSERT e SELECT na trilha, e nenhum UPDATE, DELETE ou TRUNCATE.
  • Triggers que recusam reescritas, como defesa em profundidade para sessões que detêm direitos mais amplos, com um trigger em nível de instrução para TRUNCATE, porque no PostgreSQL TRUNCATE não dispara triggers ON DELETE.
  • Um modelo de eventos imutável, no qual uma correção é um novo evento que substitui um anterior.
  • Uma convenção de substituição. O valor atual de um atributo é a entrada mais recente para ele; o histórico são todas elas.

Event sourcing é uma forma de obter um histórico somente de inserção, e não a única; ver a nota técnica.

Gravar a entrada junto com a mudança

A entrada de auditoria é gravada na mesma transação de banco de dados que a mudança de estado que ela descreve, ou por meio de um outbox transacional, para que nenhuma das duas possa existir sem a outra.

Tornar as novas tentativas idempotentes

O horário da etapa é marcado quando a etapa roda, e a mesma entrada é reenviada numa nova tentativa. As entradas recebem uma chave única natural, para que uma escrita repetida seja absorvida em vez de duplicada. No PostgreSQL, ON CONFLICT DO NOTHING pula uma linha que conflita com uma restrição ou um índice único em vez de lançar um erro, e é exatamente por isso que não pode ser a resposta inteira: um conflito nem sempre é uma nova tentativa. A mesma chave com conteúdo diferente é um segundo escritor ou um bug. A entrada armazenada deve ser comparada antes de a escrita ser tratada como nova tentativa, e a falha deve ser ruidosa quando elas diferem.

Reconstruir com uma consulta, exportar em JSON

Se a reconstrução precisa de alguém que conheça o sistema, o P-4 não vale. Ordena-se por carimbo de tempo, e declara-se o que essa ordem não resolve: entradas com o mesmo carimbo de tempo não são ordenadas por nenhum dos campos publicados.

O que um trigger não oferece

Privilégios e triggers são controles dentro da própria fronteira de confiança do banco de dados. No PostgreSQL, um superusuário ignora todas as verificações de permissão, e o dono da tabela pode desativar seus triggers. Uma tabela somente de inserção é, portanto, um controle forte contra a aplicação e fraco contra seus administradores. A detectabilidade de adulteração diante de insiders privilegiados exige um controle fora do banco de dados, e é uma propriedade separada da persistência somente de inserção. Nenhuma das duas é, por si só, uma afirmação jurídica sobre evidência.

Modos de falha

  1. O identificador gerado de novo no meio do caminho

    Um consumidor ou worker gera um identificador novo quando o campo está ausente, muitas vezes por um valor padrão num modelo de mensagem. O resultado são duas metades de uma transação e nenhuma consulta que as una.

  2. O trace existe só nos logs

    Logs são rotacionados, amostrados, não estruturados, e escritos por código que os trata como diagnóstico. Uma cadeia que só pode ser refeita a partir de logs não pode ser refeita de forma confiável.

  3. Uma fronteira assíncrona perde o contexto

    Pools de threads, executores e brokers não carregam nada a menos que algo copie o contexto. Em Python, asyncio.to_thread é documentado como propagador do contexto atual; run_in_executor não documenta nada disso, e o próprio to_thread do CPython copia o contexto explicitamente antes de chamá-lo. No Spring, um executor precisa de um task decorator que propague o contexto.

  4. Linhas armazenadas não podem ser relacionadas à sua evidência

    Nenhum source_hash, ou um hash de algo diferente dos bytes que foram de fato lidos, de modo que o mesmo insumo nunca pode ser reconhecido de novo.

  5. Novas tentativas produzem entradas ambíguas

    Um carimbo de tempo obtido no momento da inserção transforma cada nova tentativa numa entrada nova e diferente, e a trilha passa a dizer que uma etapa aconteceu duas vezes.

  6. Um conflito descartado como nova tentativa

    Uma chave única e ON CONFLICT DO NOTHING, e nada mais: uma segunda entrada com a mesma chave e uma decisão diferente desaparece sem erro, e a trilha fica com a que chegou primeiro.

  7. Uma correção sobrescreve

    Um UPDATE destrói o que se considerava antes, e a trilha deixa de conseguir explicar por que uma saída anterior disse o que disse.

  8. Uma fronteira de serviço inicia um novo trace

    Um gateway remove um cabeçalho que não conhece, uma biblioteca cliente não propaga, um job em lote começa do nada. Cada um é invisível até alguém tentar reconstruir.

  9. O backend de tracing usado como trilha de auditoria

    Sua amostragem, sua retenção e sua pipeline mutável são adequadas ao diagnóstico e inadequadas à evidência.

  10. Relógios de parede usados para ordenar etapas entre hosts

    Carimbos de tempo de máquinas diferentes só ordenam entradas na medida em que seus relógios concordam. Dentro de uma transação, um único escritor, ou uma ordem atribuída por um único escritor, é mais confiável que o horário de relógio de vários hosts.

Verificação

  • Teste de integração

    Passa quando: De uma saída publicada de volta às suas fontes: uma consulta devolve todas as entradas, todas com o mesmo trace_id, com o source_hash de cada documento lido.

    Prova de dentes: Remover o identificador de um salto assíncrono numa branch descartável. O teste de reconstrução precisa falhar, e não devolver uma cadeia mais curta que parece completa.

  • Teste de integração

    Passa quando: Uma correção acrescenta: depois dela, as duas entradas existem, e a posterior substitui a anterior.

    Prova de dentes: Trocar a inserção por uma atualização: o teste falha.

  • Teste de integração

    Passa quando: Uma nova tentativa idêntica é absorvida, e a mesma chave com conteúdo diferente é recusada, não absorvida.

    Prova de dentes: Tratar todo conflito como nova tentativa sem comparar: o teste que grava conteúdo diferente sob a mesma chave falha.

  • Teste de integração

    Passa quando: Contra o banco de dados real, o papel da aplicação não consegue atualizar, apagar nem truncar a trilha.

    Prova de dentes: Conceder UPDATE ao papel num banco de dados descartável: o teste falha.

  • Teste de contrato

    Passa quando: Todo esquema de mensagem que atravessa uma fronteira assíncrona exige o identificador de auditoria.

    Prova de dentes: Tornar o campo opcional: o teste de contrato falha.

  • Teste de ponta a ponta

    Passa quando: Uma requisição que se desdobra num job e numa mensagem mantém uma única identidade de auditoria, verificada na trilha persistida e não nos logs.

  • Evidência manual

    Passa quando: Exportar uma cadeia em JSON e pedir a uma pessoa que não construiu o sistema que reconstrua, só a partir dela, o histórico da saída.

Realizações alternativas

  • Event sourcing. O event store é o histórico, e o estado é derivado dele. Propriedades de auditoria fortes, e um grande compromisso de arquitetura.
  • Change data capture. Mudanças de linha lidas do log do banco de dados. Útil para replicação; registra o que mudou, não quem decidiu nem por quê, então não substitui as entradas de auditoria de negócio.
  • Armazenamento de objetos de gravação única para cadeias exportadas, onde a retenção precisa sobreviver ao banco de dados.
  • Armazenamentos de ledger ou com detectabilidade de adulteração, onde a adulteração por insiders faz parte do modelo de ameaças.

Limitações

  • O padrão não decide quais etapas são relevantes para uma regulamentação específica ou um produto específico.
  • Persistência somente de inserção não é detectabilidade de adulteração, e nenhuma das duas é uma conclusão jurídica sobre evidência.
  • Ele não define prazos de retenção.
  • Ele depende de que todo componente honre o contrato. Um componente que não o honra é encontrado pelo teste de reconstrução, não impedido por ele.

Fontes

COADF Engineering Companion 1.0 · não normativo · corresponde ao COADF Core 2.2

Direitos de publicação reservados. Nenhuma licença pública é concedida, no momento, para o COADF Engineering Companion 1.0 nem para seus exemplos de referência.

Status de propriedade intelectual e de publicação