Zum Inhalt springen

Unabhängiges F&E-Projekt · Köln

Durchgängige Nachvollziehbarkeit

Eine nachverfolgbare Identität verbindet die relevanten Schritte von der Aufnahme bis zur resultierenden Ausgabe.

Nicht normativ

Companion-Version
1.0
Bezug zu COADF Core
2.2
Status
Aktuell
Zuletzt geprüft
COADF-Prinzipien
P-4

Architektureigenschaft

COADF P-4 ist vollständig veröffentlicht. Eine Kennung entsteht, wenn ein Dokument aufgenommen wird, und begleitet jeden Verarbeitungsschritt, jede Konfidenzbewertung, jede menschliche Entscheidung und die veröffentlichte Ausgabe. Die Spur wächst nur an, eine einzige Abfrage rekonstruiert die Kette, und die Kette lässt sich als JSON exportieren, für jemanden, der das System nicht gebaut hat. Das veröffentlichte Schema der Prüfspur benennt die Felder: trace_id, timestamp, event_type, actor, input, output, decision, source_hash und immutable.

Zwei Eigenschaften tragen das. Kontinuität: Jeder Schritt trägt dieselbe Identität, über jede Grenze hinweg, die er überschreitet. Beständigkeit: Der Eintrag zu jedem Schritt bleibt erhalten, und niemand kann ihn nachträglich bearbeiten, damit die Antwort besser aussieht.

Warum sie wichtig ist

Die Frage, die P-4 beantwortet, kommt spät und von außen. Monate nach der Veröffentlichung fragt jemand, woher ein Wert stammt, was ihn gelesen hat und wer ihn sich angesehen hat. Die Antwort muss sich von jemandem rekonstruieren lassen, der das System nicht gebaut hat, aus Einträgen, die in der Zwischenzeit niemand umschreiben konnte.

Ein verteilter Trace ist keine fachliche Prüfspur

Diese Unterscheidung verwischen die meisten Implementierungen, weil die Werkzeuge einander ähneln: Beide haben eine Kennung, beide überschreiten Dienstgrenzen, beide zeigen Schritte der Reihe nach. Sie beantworten verschiedene Fragen und geben verschiedene Zusagen.

Verteilter Trace und Prüfspur im Vergleich
AspektVerteilter TracePrüfspur
ZweckEine Ausführung erklären: Latenz, Fehler, AufrufstrukturFeststellen, was mit einem Vorgang geschah und wer gehandelt hat
IdentitätEin Trace pro Anfrage oder Job, im W3C-Header traceparent mitgeführtEine trace_id pro fachlichem Vorgang, bei der Aufnahme erzeugt
LebensdauerDie Dauer der Anfrage; Aufbewahrung in Tagen gemessenSo lange wie die Datensätze, die sie begründet
SamplingSampling ist üblich und zulässigKein Sampling: Ein fehlender Eintrag ist ein Defekt
VeränderbarkeitPipelines filtern, verwerfen und schreiben per Konfiguration umAppend-only: Eine Berichtigung ist ein neuer Eintrag
InhaltSpans, Zeitmessungen, Attribute, gewählt für die DiagnoseFachliche Ereignisse: Eingabe, Ausgabe, Entscheidung, Akteur

Die W3C-Spezifikation Trace Context definiert den Header traceparent, und OpenTelemetry-SDKs geben standardmäßig tracecontext und baggage weiter. Diese Identität gilt pro Ausführung. In OpenTelemetry wird ein Trace, der nicht gesampelt wird, nicht exportiert, und der Filter-Prozessor des Collectors verwirft Telemetrie, die eine Bedingung erfüllt. Beides ist für die Diagnose korrektes Verhalten und schließt eine Verwendung als Nachweis aus.

Die beiden lassen sich korrelieren: die trace_id der Prüfspur als Attribut auf jedem Span festhalten und die Kennung des verteilten Trace in den Logs. Keines von beiden darf zum anderen gemacht werden. Ein einzelner fachlicher Vorgang erstreckt sich regelmäßig über mehrere verteilte Traces: eine Aufnahmeanfrage, einen asynchronen Verarbeitungsjob, eine Tage später getroffene Entscheidung, die Ausgabe.

Korrelation kopiert die Kennung der Prüfspur in die Telemetrie, und Telemetrie überschreitet weitere Grenzen: Exporter, Anbieter, Zugriffsregeln und Aufbewahrungsfristen, die meist lockerer sind als die des Speichers der Prüfspur. Die Kennung wird klassifiziert, bevor sie hinausgeht: eine opake Referenz, nie personenbezogene Daten und nie ein Geheimnis. Wo die Kennung der Prüfspur selbst eine Bedeutung trägt, wird über eine getrennte, nicht sensible Referenz korreliert.

Beispielarchitektur · Nicht normativ

Verteilte Traces und eine Prüfspur

Beispielarchitektur, nicht normativ: oben vier getrennte verteilte Traces, einer je Anfrage oder Job, und darunter eine Prüfspur, deren illustrative Einträge eine einzige trace_id teilen.Obere Bahn, verteilte Traces: vier getrennte Traces, je einer für eine Aufnahme-Anfrage, einen Verarbeitungsjob, eine Tage später getroffene Entscheidung und eine Ausgabe. Sie sind nicht miteinander verbunden. Untere Bahn, die Prüfspur: vier illustrative Einträge, Aufnahme, Verarbeitung, Entscheidung und Ausgabe, alle mit derselben trace_id, getrennt von der Telemetrie persistiert und nicht gesampelt. Eine Notiz in der Bahn besagt, dass die Ereignisse illustrativ sind und kein von COADF vorausgesetzter Workflow. Eine gestrichelte Linie verbindet jeden Trace mit dem Eintrag, dem er entspricht, beschriftet als Span-Attribut, das die trace_id der Prüfspur trägt.Verteilte Traces: Ausführungskontext, kann gesampelt werden, kurzlebigPrüfspur: eine trace_id, Append-only, nicht gesampeltTrace AAufnahme-AnfrageTrace BVerarbeitungsjobTrace CEntscheidung, Tage späterTrace DAusgabeAufnahmeVerarbeitungEntscheidungAusgabeIllustrative Ereignisse, kein von COADF vorausgesetzter Workflow; dieselbe trace_id in jedem EintragSpan-Attribut
  • Korrelation über die trace_id der Prüfspur
Ein Geschäftsvorgang, vier verteilte Traces. Telemetrie kann gesampelt werden und ist kurzlebig; Einträge der Prüfspur werden getrennt persistiert und nicht gesampelt. Sie werden über die trace_id der Prüfspur korreliert, die an jedem Trace als Span-Attribut erfasst ist. Die Ereignisse sind illustrativ, kein von COADF vorausgesetzter Workflow.

Textbeschreibung. Obere Bahn, verteilte Traces: vier getrennte Traces, je einer für eine Aufnahme-Anfrage, einen Verarbeitungsjob, eine Tage später getroffene Entscheidung und eine Ausgabe. Sie sind nicht miteinander verbunden. Untere Bahn, die Prüfspur: vier illustrative Einträge, Aufnahme, Verarbeitung, Entscheidung und Ausgabe, alle mit derselben trace_id, getrennt von der Telemetrie persistiert und nicht gesampelt. Eine Notiz in der Bahn besagt, dass die Ereignisse illustrativ sind und kein von COADF vorausgesetzter Workflow. Eine gestrichelte Linie verbindet jeden Trace mit dem Eintrag, dem er entspricht, beschriftet als Span-Attribut, das die trace_id der Prüfspur trägt.

Zulässige Umsetzungsstrategien

Einmal erzeugen, bei der Aufnahme

Die Kennung entsteht dort, wo der Vorgang eintritt, und nirgends sonst. Jede spätere Komponente verlangt sie, und keine erzeugt eine. Eine fehlende Kennung ist ein Fehler, nie ein Grund, eine neue zu erzeugen, und ein Standardwert in einem Nachrichtenmodell ist der häufigste Weg, diese Regel zu brechen, ohne dass es jemand bemerkt.

Explizit über jede Grenze mitführen

  • Im Prozess: ein Funktionsargument oder der Anfragekontext.
  • Zwischen Diensten: ein Feld im Anfrage- oder Nachrichtenvertrag, wo ein Schema es verlangen kann.
  • In geplanten Aufgaben: ein Job-Argument.
  • Im Speicher: eine Spalte in jeder gespeicherten Zeile.

Propagation-Bibliotheken führen den Ausführungskontext für einen mit. Die Identität der Prüfspur ist Teil des eigenen Vertrags und gehört in die Nutzlast.

Dauerhaft speichern, nur anhängend

Mehrere Umsetzungen sind zulässig, und sie lassen sich kombinieren:

  • Persistenz nur durch Einfügen. Das Repository bietet Anhängen und Lesen an und sonst nichts.
  • Datenbankrechte. Die Rolle der Anwendung hat INSERT und SELECT auf der Prüfspur und kein UPDATE, DELETE oder TRUNCATE.
  • Trigger, die Umschreibungen verweigern, als zusätzliche Verteidigungslinie für Sitzungen, die doch weitergehende Rechte haben, mit einem Trigger auf Anweisungsebene für TRUNCATE, denn in PostgreSQL löst TRUNCATE keine ON DELETE-Trigger aus.
  • Ein unveränderliches Ereignismodell, in dem eine Berichtigung ein neues Ereignis ist, das ein früheres ablöst.
  • Eine Ablösungskonvention. Der aktuelle Wert eines Attributs ist der neueste Eintrag dazu; die Historie sind alle Einträge.

Event Sourcing ist ein Weg zu einer Append-only-Historie, und nicht der einzige; siehe die Technologie-Notiz.

Den Eintrag mit der Änderung schreiben

Den Eintrag der Prüfspur in derselben Datenbanktransaktion festhalten wie die Zustandsänderung, die er beschreibt, oder über eine transaktionale Outbox, sodass keines ohne das andere existieren kann.

Wiederholungen idempotent machen

Den Zeitpunkt des Schritts festhalten, wenn der Schritt läuft, und bei einer Wiederholung denselben Eintrag erneut senden. Einträge erhalten einen natürlichen eindeutigen Schlüssel, damit ein wiederholter Schreibvorgang aufgefangen statt verdoppelt wird. In PostgreSQL überspringt ON CONFLICT DO NOTHING eine Zeile, die mit einem Unique-Constraint oder -Index kollidiert, statt einen Fehler auszulösen, und genau deshalb kann es nicht die ganze Antwort sein: Ein Konflikt ist nicht immer eine Wiederholung. Derselbe Schlüssel mit anderem Inhalt ist ein zweiter Schreiber oder ein Bug. Den gespeicherten Eintrag vergleichen, bevor der Schreibvorgang als Wiederholung gilt, und deutlich fehlschlagen, wenn er abweicht.

Mit einer Abfrage rekonstruieren, als JSON exportieren

Braucht die Rekonstruktion jemanden, der das System kennt, gilt P-4 nicht. Nach Zeitstempel ordnen und angeben, was diese Reihenfolge nicht klärt: Einträge mit demselben Zeitstempel sind durch keines der veröffentlichten Felder geordnet.

Was ein Trigger nicht leistet

Rechte und Trigger sind Kontrollen innerhalb der eigenen Vertrauensgrenze der Datenbank. In PostgreSQL umgeht ein Superuser alle Rechteprüfungen, und der Eigentümer der Tabelle kann ihre Trigger deaktivieren. Eine Append-only-Tabelle ist daher eine starke Kontrolle gegenüber der Anwendung und eine schwache gegenüber ihren Administratoren. Manipulationsnachweis gegenüber privilegierten Innentätern braucht eine Kontrolle außerhalb der Datenbank, und er ist eine eigene Eigenschaft, getrennt von Append-only-Persistenz. Keines von beiden ist für sich eine rechtliche Aussage über Beweise.

Fehlermuster

  1. Die Kennung auf halbem Weg neu erzeugt

    Ein Konsument oder Worker erzeugt eine neue Kennung, wenn das Feld fehlt, oft über einen Standardwert in einem Nachrichtenmodell. Das Ergebnis sind zwei Hälften eines Vorgangs und keine Abfrage, die sie verbindet.

  2. Die Spur existiert nur in Logs

    Logs rotieren, werden gesampelt, sind unstrukturiert und werden von Code geschrieben, der sie als Diagnose behandelt. Eine Kette, die sich nur aus Logs wieder aufbauen lässt, lässt sich nicht verlässlich wieder aufbauen.

  3. Eine asynchrone Grenze verliert den Kontext

    Thread-Pools, Executors und Broker führen nichts mit, solange nichts es kopiert. In Python ist für asyncio.to_thread dokumentiert, dass es den aktuellen Kontext weitergibt; für run_in_executor ist nichts dergleichen dokumentiert, und das to_thread von CPython selbst kopiert den Kontext ausdrücklich, bevor es die Funktion aufruft. In Spring braucht ein Executor einen Task-Decorator, der den Kontext weitergibt.

  4. Gespeicherte Zeilen lassen sich nicht mit ihren Nachweisen verbinden

    Kein source_hash, oder ein Hash über etwas anderes als die tatsächlich gelesenen Bytes, sodass dieselbe Eingabe nie wiedererkannt werden kann.

  5. Wiederholungen erzeugen mehrdeutige Einträge

    Ein Zeitstempel, der beim Einfügen genommen wird, macht aus jeder Wiederholung einen neuen, anderen Eintrag, und die Spur sagt dann, ein Schritt sei zweimal geschehen.

  6. Ein Konflikt als Wiederholung verworfen

    Ein eindeutiger Schlüssel und ON CONFLICT DO NOTHING, sonst nichts: Ein zweiter Eintrag mit demselben Schlüssel und einer anderen Entscheidung verschwindet ohne Fehler, und die Spur behält den, der zuerst ankam.

  7. Eine Berichtigung überschreibt

    Ein UPDATE zerstört, was zuvor angenommen wurde, und die Spur kann nicht mehr erklären, warum eine frühere Ausgabe sagte, was sie sagte.

  8. Eine Dienstgrenze beginnt einen neuen Trace

    Ein Gateway entfernt einen Header, den es nicht kennt, eine Client-Bibliothek gibt nichts weiter, ein Batch-Job beginnt bei null. Jedes davon bleibt unsichtbar, bis jemand zu rekonstruieren versucht.

  9. Das Tracing-Backend als Prüfspur verwendet

    Sein Sampling, seine Aufbewahrung und seine veränderbare Pipeline sind für die Diagnose richtig und für Nachweise falsch.

  10. Systemuhren zum Ordnen von Schritten über mehrere Hosts verwendet

    Zeitstempel verschiedener Maschinen ordnen Einträge nur so gut, wie ihre Uhren übereinstimmen. Innerhalb eines Vorgangs ist ein einzelner Schreiber, oder eine von einem einzigen Schreiber vergebene Reihenfolge, verlässlicher als die Uhrzeit mehrerer Hosts.

Verifikation

  • Integrationstest

    Bestanden, wenn: Von einer veröffentlichten Ausgabe zurück zu ihren Quellen: Eine Abfrage liefert jeden Eintrag, alle mit derselben trace_id, mit dem source_hash jedes gelesenen Dokuments.

    Zahnbeweis: In einem Arbeitszweig die Kennung an einem asynchronen Übergang weglassen. Der Rekonstruktionstest muss fehlschlagen, statt eine kürzere Kette zu liefern, die vollständig aussieht.

  • Integrationstest

    Bestanden, wenn: Eine Berichtigung hängt an: Danach existieren beide Einträge, und der spätere löst den früheren ab.

    Zahnbeweis: Das Anhängen durch ein Update ersetzen: Der Test schlägt fehl.

  • Integrationstest

    Bestanden, wenn: Eine exakte Wiederholung wird aufgefangen, und derselbe Schlüssel mit anderem Inhalt wird abgewiesen, nicht aufgefangen.

    Zahnbeweis: Jeden Konflikt ohne Vergleich als Wiederholung behandeln: Der Test, der unter demselben Schlüssel anderen Inhalt schreibt, schlägt fehl.

  • Integrationstest

    Bestanden, wenn: Gegen die echte Datenbank kann die Rolle der Anwendung die Prüfspur weder aktualisieren noch löschen noch leeren.

    Zahnbeweis: In einer Wegwerf-Datenbank der Rolle UPDATE gewähren: Der Test schlägt fehl.

  • Vertragstest

    Bestanden, wenn: Jedes Nachrichtenschema, das eine asynchrone Grenze überschreitet, verlangt die Kennung der Prüfspur.

    Zahnbeweis: Das Feld optional machen: Der Vertragstest schlägt fehl.

  • End-to-End-Test

    Bestanden, wenn: Eine Anfrage, die sich auf einen Job und eine Nachricht verzweigt, behält eine einzige Identität für die Prüfspur, geprüft in der gespeicherten Spur und nicht in den Logs.

  • Manueller Nachweis

    Bestanden, wenn: Eine Kette als JSON exportieren und von einer Person, die das System nicht gebaut hat, allein daraus die Geschichte der Ausgabe rekonstruieren lassen.

Alternative Umsetzungen

  • Event Sourcing. Der Event Store ist die Historie, und der Zustand wird daraus abgeleitet. Starke Eigenschaften für die Prüfspur und eine große architektonische Festlegung.
  • Change Data Capture. Zeilenänderungen, aus dem Log der Datenbank gelesen. Nützlich für Replikation; es hält fest, was sich geändert hat, nicht wer entschieden hat oder warum, und ersetzt daher keine fachlichen Einträge der Prüfspur.
  • Write-once-Objektspeicher für exportierte Ketten, wo die Aufbewahrung die Datenbank überdauern muss.
  • Ledger- oder manipulationsnachweisende Speicher, wo Manipulation durch Innentäter zum Bedrohungsmodell gehört.

Grenzen

  • Das Muster entscheidet nicht, welche Schritte für eine bestimmte Regulierung oder ein bestimmtes Produkt relevant sind.
  • Append-only-Persistenz ist kein Manipulationsnachweis, und keines von beiden ist eine rechtliche Schlussfolgerung über Beweise.
  • Es legt keine Aufbewahrungsfristen fest.
  • Es setzt voraus, dass jede Komponente den Vertrag einhält. Eine Komponente, die das nicht tut, wird vom Rekonstruktionstest gefunden, nicht durch ihn verhindert.

Quellen

COADF Engineering Companion 1.0 · nicht normativ · bezieht sich auf COADF Core 2.2

Veröffentlichungsrechte vorbehalten. Für den COADF Engineering Companion 1.0 und seine Referenzbeispiele wird derzeit keine öffentliche Lizenz erteilt.

Schutzrechts- und Veröffentlichungsstatus