Ir al contenido

Proyecto independiente de I+D · Colonia

Trazabilidad de extremo a extremo

Una sola identidad trazable une los pasos relevantes desde la recepción hasta la salida resultante.

No normativo

Versión del Companion
1.0
Corresponde a COADF Core
2.2
Estado
Vigente
Última revisión
Principios de COADF
P-4

Propiedad de arquitectura

COADF P-4 se publica íntegro. Al recibir un documento se crea un identificador que viaja con cada paso de procesamiento, cada evaluación de confianza, cada decisión humana y la salida publicada. La traza es de solo inserción, una consulta reconstruye la cadena y la cadena se exporta como JSON para alguien que no construyó el sistema. El esquema publicado de la traza de auditoría nombra los campos: trace_id, timestamp, event_type, actor, input, output, decision, source_hash e immutable.

Dos propiedades la sostienen. Continuidad: cada paso lleva la misma identidad, a través de cada frontera que cruza. Persistencia: el registro de cada paso sobrevive, y nadie puede editarlo después para que la respuesta parezca mejor.

Por qué importa

La pregunta que responde P-4 llega tarde y desde fuera. Meses después de la publicación alguien pregunta de dónde salió un valor, qué lo leyó y quién lo miró. La respuesta tiene que poder reconstruirla una persona que no construyó el sistema, a partir de registros que nadie pudo reescribir entretanto.

Una traza distribuida no es una traza de auditoría de negocio

Es la distinción que más implementaciones difuminan, porque las herramientas se parecen: ambas tienen un identificador, ambas cruzan servicios, ambas muestran pasos en secuencia. Responden a preguntas distintas y prometen cosas distintas.

Traza distribuida y traza de auditoría, comparadas
AspectoTraza distribuidaTraza de auditoría
FinalidadExplicar una ejecución: latencia, errores, estructura de llamadasEstablecer qué le ocurrió a una transacción y quién actuó
IdentidadUna traza por petición o tarea, transportada en la cabecera W3C traceparentUn trace_id por transacción de negocio, creado en la recepción
DuraciónLa de la petición; conservación medida en díasTanto como los registros que justifica
MuestreoEl muestreo es normal y está permitidoSin muestreo: una entrada ausente es un defecto
MutabilidadLas pipelines filtran, descartan y reescriben por configuraciónSolo inserción: una corrección es una entrada nueva
ContenidoSpans, tiempos, atributos elegidos para el diagnósticoEventos de negocio: entrada, salida, decisión, actor

La especificación W3C Trace Context define la cabecera traceparent, y los SDK de OpenTelemetry propagan tracecontext y baggage por defecto. Esa identidad es por ejecución. En OpenTelemetry, una traza que no se muestrea no se exporta, y el procesador de filtrado del Collector descarta la telemetría que cumple una condición. Ambos son comportamientos correctos para el diagnóstico y descalificantes para la evidencia.

Las dos se correlacionan: se registra el trace_id de auditoría en cada span como atributo, y el identificador de la traza distribuida en los logs. Ninguna debe convertirse en la otra. Una sola transacción de negocio abarca habitualmente varias trazas distribuidas: una petición de recepción, una tarea de procesamiento asíncrona, una decisión tomada días después, la salida.

La correlación copia el identificador de auditoría en la telemetría, y la telemetría cruza otras fronteras: exportadores, proveedores, reglas de acceso y plazos de conservación que suelen ser más laxos que los del almacén de auditoría. Hay que clasificar el identificador antes de que salga: una referencia opaca, nunca datos personales ni un secreto. Donde el identificador de auditoría tenga significado propio, la correlación se hace mediante una referencia separada y no sensible.

Arquitectura de ejemplo · No normativa

Trazas distribuidas y una sola traza de auditoría

Arquitectura de ejemplo, no normativa: arriba, cuatro trazas distribuidas separadas, una por solicitud o trabajo, y debajo una traza de auditoría cuyas entradas ilustrativas comparten un único trace_id.Carril superior, trazas distribuidas: cuatro trazas separadas, una para una solicitud de recepción, otra para un trabajo de procesamiento, otra para una decisión tomada días después y otra para una salida. No están conectadas entre sí. Carril inferior, la traza de auditoría: cuatro entradas ilustrativas, recepción, procesamiento, decisión y salida, todas con el mismo trace_id, persistidas por separado de la telemetría y no muestreadas. Una nota en el carril dice que los eventos son ilustrativos, no un flujo de trabajo exigido por COADF. Una línea discontinua une cada traza con la entrada que le corresponde, rotulada como atributo de span que lleva el trace_id de auditoría.Trazas distribuidas: contexto de ejecución, muestreables, de corta duraciónTraza de auditoría: un trace_id, de solo inserción, sin muestreoTraza Asolicitud de recepciónTraza Btrabajo de procesamientoTraza Cdecisión, días despuésTraza DsalidarecepciónprocesamientodecisiónsalidaEventos ilustrativos, no un flujo de trabajo exigido por COADF; el mismo trace_id en cada entradaatributo de span
  • Correlación mediante el trace_id de auditoría
Una transacción de negocio, cuatro trazas distribuidas. La telemetría puede muestrearse y es de corta duración; las entradas de auditoría se persisten por separado y no se muestrean. Se correlacionan mediante el trace_id de auditoría, registrado en cada traza como atributo de span. Los eventos son ilustrativos, no un flujo de trabajo exigido por COADF.

Descripción textual. Carril superior, trazas distribuidas: cuatro trazas separadas, una para una solicitud de recepción, otra para un trabajo de procesamiento, otra para una decisión tomada días después y otra para una salida. No están conectadas entre sí. Carril inferior, la traza de auditoría: cuatro entradas ilustrativas, recepción, procesamiento, decisión y salida, todas con el mismo trace_id, persistidas por separado de la telemetría y no muestreadas. Una nota en el carril dice que los eventos son ilustrativos, no un flujo de trabajo exigido por COADF. Una línea discontinua une cada traza con la entrada que le corresponde, rotulada como atributo de span que lleva el trace_id de auditoría.

Estrategias de implementación válidas

Crear una sola vez, en la recepción

El identificador se crea donde entra la transacción y en ningún otro sitio. Todo componente posterior lo exige y ninguno lo genera. Un identificador ausente es un error, nunca un motivo para crear uno nuevo, y un valor por defecto en un modelo de mensaje es la forma más común de romper esa regla sin que nadie lo note.

Transportarlo de forma explícita a través de cada frontera

  • Dentro del proceso: un argumento de función o el contexto de la petición.
  • Entre servicios: un campo del contrato de la petición o del mensaje, donde un esquema puede exigirlo.
  • En el trabajo programado: un argumento de la tarea.
  • En reposo: una columna en cada fila almacenada.

Las bibliotecas de propagación transportan por su cuenta el contexto de ejecución. La identidad de auditoría forma parte del contrato propio y pertenece a la carga útil.

Persistir, solo inserción

Hay varias realizaciones válidas, y se combinan:

  • Persistencia de solo inserción. El repositorio ofrece añadir y leer, y nada más.
  • Privilegios de base de datos. El rol de la aplicación tiene INSERT y SELECT sobre la traza, y no UPDATE, DELETE ni TRUNCATE.
  • Triggers que rechazan reescrituras, como defensa en profundidad para las sesiones que sí tienen derechos más amplios, con un trigger a nivel de sentencia para TRUNCATE, porque en PostgreSQL TRUNCATE no dispara los triggers ON DELETE.
  • Un modelo de eventos inmutable, en el que una corrección es un evento nuevo que sustituye a uno anterior.
  • Una convención de sustitución. El valor vigente de un atributo es su última entrada; el historial son todas.

El event sourcing es una manera de obtener un historial de solo inserción, y no la única; véase la nota técnica.

Escribir la entrada con el cambio

La entrada de auditoría se registra en la misma transacción de base de datos que el cambio de estado que describe, o mediante un outbox transaccional, de modo que ninguno de los dos pueda existir sin el otro.

Hacer idempotentes los reintentos

La hora del paso se fija cuando el paso se ejecuta, y en un reintento se reenvía la misma entrada. Las entradas llevan una clave única natural para que una escritura repetida se absorba en lugar de duplicarse. En PostgreSQL, ON CONFLICT DO NOTHING omite una fila que entra en conflicto con una restricción o un índice únicos en lugar de lanzar un error, y justo por eso no puede ser toda la respuesta: un conflicto no siempre es un reintento. La misma clave con otro contenido es un segundo escritor o un error. Hay que comparar la entrada almacenada antes de llamar reintento a la escritura, y fallar de forma visible cuando difiere.

Reconstruir con una consulta, exportar como JSON

Si la reconstrucción necesita a alguien que conozca el sistema, P-4 no se cumple. Se ordena por marca de tiempo, y se dice lo que ese orden no resuelve: las entradas con la misma marca de tiempo no quedan ordenadas por ninguno de los campos publicados.

Lo que un trigger no da

Los privilegios y los triggers son controles dentro de la propia frontera de confianza de la base de datos. En PostgreSQL, un superusuario omite todas las comprobaciones de permisos, y el propietario de la tabla puede desactivar sus triggers. Una tabla de solo inserción es, por tanto, un control fuerte frente a la aplicación y uno débil frente a sus administradores. Hacer detectable la manipulación por parte de personas internas con privilegios necesita un control fuera de la base de datos, y es una propiedad distinta de la persistencia de solo inserción. Ninguna de las dos es, por sí sola, una afirmación jurídica sobre la evidencia.

Modos de fallo

  1. El identificador se regenera a mitad de camino

    Un consumidor o un worker crea un identificador nuevo cuando falta el campo, a menudo mediante un valor por defecto en un modelo de mensaje. El resultado son dos mitades de una transacción y ninguna consulta que las una.

  2. La traza existe solo en los logs

    Los logs rotan, se muestrean, no tienen estructura y los escribe código que los trata como diagnóstico. Una cadena que solo puede reconstruirse a partir de logs no puede reconstruirse de forma fiable.

  3. Una frontera asíncrona pierde el contexto

    Los pools de hilos, los executors y los brokers no transportan nada salvo que algo lo copie. En Python, asyncio.to_thread está documentado como propagador del contexto actual; run_in_executor no documenta nada parecido, y la propia to_thread de CPython copia el contexto de forma explícita antes de llamarlo. En Spring, un executor necesita un decorador de tareas que propague el contexto.

  4. Las filas almacenadas no pueden relacionarse con su evidencia

    No hay source_hash, o hay un hash de algo distinto de los bytes que realmente se leyeron, así que la misma entrada nunca puede volver a reconocerse.

  5. Los reintentos producen entradas ambiguas

    Una marca de tiempo tomada en el momento de la inserción convierte cada reintento en una entrada nueva y distinta, y la traza dice entonces que un paso ocurrió dos veces.

  6. Un conflicto descartado como reintento

    Una clave única y ON CONFLICT DO NOTHING, y nada más: una segunda entrada con la misma clave y otra decisión desaparece sin error, y la traza conserva la que llegó primero.

  7. Una corrección sobrescribe

    Un UPDATE destruye lo que se creía antes, y la traza ya no puede explicar por qué una salida anterior decía lo que decía.

  8. Una frontera de servicio inicia una traza nueva

    Un gateway elimina una cabecera que no conoce, una biblioteca cliente no propaga, una tarea por lotes empieza desde cero. Cada caso es invisible hasta que alguien intenta reconstruir.

  9. El backend de tracing usado como traza de auditoría

    Su muestreo, su conservación y su pipeline mutable son adecuados para el diagnóstico e inadecuados para la evidencia.

  10. Relojes de pared usados para ordenar pasos entre hosts

    Las marcas de tiempo de máquinas distintas ordenan las entradas solo en la medida en que sus relojes coinciden. Dentro de una transacción, un único escritor, o un orden asignado por uno solo, es más fiable que la hora de pared de varios hosts.

Verificación

  • Prueba de integración

    Pasa cuando: Desde una salida publicada hasta sus fuentes: una consulta devuelve todas las entradas, todas con el mismo trace_id, con el source_hash de cada documento leído.

    Prueba de dientes: Se quita el identificador de un salto asíncrono en una rama de trabajo. La prueba de reconstrucción tiene que fallar, no devolver una cadena más corta que parece completa.

  • Prueba de integración

    Pasa cuando: Una corrección se añade: después existen ambas entradas, y la posterior sustituye a la anterior.

    Prueba de dientes: Se reemplaza la inserción por una actualización: la prueba falla.

  • Prueba de integración

    Pasa cuando: Un reintento exacto se absorbe, y la misma clave con otro contenido se rechaza, no se absorbe.

    Prueba de dientes: Se toma cada conflicto como reintento sin comparar: falla la prueba que escribe otro contenido bajo la misma clave.

  • Prueba de integración

    Pasa cuando: Contra la base de datos real, el rol de la aplicación no puede actualizar, borrar ni truncar la traza.

    Prueba de dientes: Se concede UPDATE al rol en una base de datos de trabajo: la prueba falla.

  • Prueba de contrato

    Pasa cuando: Todo esquema de mensaje que cruza una frontera asíncrona exige el identificador de auditoría.

    Prueba de dientes: Se hace opcional el campo: la prueba de contrato falla.

  • Prueba de extremo a extremo

    Pasa cuando: Una petición que se ramifica en una tarea y un mensaje conserva una sola identidad de auditoría, comprobada en la traza persistida y no en los logs.

  • Evidencia manual

    Pasa cuando: Se exporta una cadena como JSON y una persona que no construyó el sistema reconstruye solo a partir de ella la historia de la salida.

Realizaciones alternativas

  • Event sourcing. El almacén de eventos es el historial y el estado se deriva de él. Propiedades de auditoría sólidas, y un compromiso de arquitectura grande.
  • Captura de cambios de datos. Cambios de filas leídos del log de la base de datos. Útil para la replicación; registra qué cambió, no quién decidió ni por qué, así que no sustituye las entradas de auditoría de negocio.
  • Almacenamiento de objetos de escritura única para las cadenas exportadas, donde la conservación tiene que durar más que la base de datos.
  • Almacenes de tipo libro mayor o que hacen detectable la manipulación, donde la manipulación por personas internas forma parte del modelo de amenazas.

Limitaciones

  • El patrón no decide qué pasos son relevantes para una regulación concreta o un producto concreto.
  • La persistencia de solo inserción no equivale a detectabilidad de manipulaciones, y ninguna de las dos es una conclusión jurídica sobre la evidencia.
  • No define plazos de conservación.
  • Depende de que cada componente respete el contrato. Un componente que no lo hace lo detecta la prueba de reconstrucción; no lo impide.

Fuentes

COADF Engineering Companion 1.0 · no normativo · corresponde a COADF Core 2.2

Derechos de publicación reservados. Por ahora no se concede ninguna licencia pública para el COADF Engineering Companion 1.0 ni para sus ejemplos de referencia.

Estado de propiedad intelectual y de publicación