Zum Inhalt springen

FORGE · Technik im Detail

Ereignisströme laufen geordnet und ohne Doppel in die Ontologie.

Wie FORGE Ereignisströme aus einem Log liest, die Ordnung je Schlüssel hält, Doppel über Nummern verwirft und Verspätete als Korrektur ablegt.

Nicht jede Quelle liefert Tabellen. Manche liefert einen Strom von Ereignissen: Messwerte, Statusmeldungen, Buchungen, die einzeln und laufend ankommen. FORGE nimmt solche Ströme über ein Log wie Apache Kafka entgegen und legt sie so in die Ontologie, dass die Ordnung je Objekt stimmt und kein Ereignis zweimal wirkt, auch wenn es zweimal ankommt. So ist die Verarbeitung ausgelegt.

Diese Seite beschreibt die Bauweise des Systems. Gebaut sind der Prototyp von Freigabe und Protokoll und der Demonstrator von KALKÜL; den ganzen Stand finden Sie unter Was heute steht.

Auf dieser Seite
  1. 01Ordnung gilt je Schlüssel
  2. 02Doppel erkennen
  3. 03Verspätete Ereignisse
  4. 04Zustand und Wiederanlauf
  5. 05Rückstau und nicht lesbare Ereignisse
  6. 06Eckdaten
  7. 07Weiter

Ordnung gilt je Schlüssel

Ein Log wie Kafka teilt einen Strom in Partitionen. Innerhalb einer Partition gilt die Reihenfolge, in der Ereignisse angehängt wurden; über Partitionen hinweg gibt es keine. Welche Ereignisse geordnet bleiben, bestimmt darum der Schlüssel. Der Sender setzt als Schlüssel die Identität des Objekts, und alle Ereignisse eines Objekts landen in derselben Partition. Mehr Ordnung braucht die Ontologie nicht, und mehr bietet ein verteiltes Log nicht ohne Engpass.

Die Zuordnung von Schlüssel zu Partition hängt von der Zahl der Partitionen ab. Wer sie später erhöht, ändert die Zuordnung; Ereignisse desselben Objekts können dann in zwei Partitionen liegen, alte und neue. FORGE legt die Zahl beim Anbinden mit Reserve fest und behandelt eine Änderung wie eine Migration: neuer Strom, Übergang, Umschalten.

Jedes Ereignis trägt zwei Zeiten, die Ereigniszeit, wann es in der Welt geschah, und die Aufnahmezeit, wann FORGE es annahm. In der Ontologie wird die Ereigniszeit zur Gültigkeitszeit und die Aufnahmezeit zur Erfassungszeit. Die Reihenfolge im Log bestimmt, in welcher Folge Ereignisse eines Schlüssels angewandt werden.

Strom im Schnitt

Unten die Quelle, darüber das Log mit Partitionen je Schlüssel. Die Verarbeitung hält Fenster und Zustand, die Ontologie nimmt Aussagen in der Reihenfolge je Objekt an.

Ereignisstrom in FORGE im Schnitt: Quelle, Log, Verarbeitung, OntologieUnten die Quelle, darüber das Log mit Partitionen je Schlüssel. Die Verarbeitung hält Fenster und Zustand, die Ontologie nimmt Aussagen in der Reihenfolge je Objekt an.OntologieJE OBJEKT GEORDNETAUSSAGEVerarbeitungMIT ZUSTANDFENSTERZUSTANDLogPARTITION JE SCHLÜSSELPARTITIONOFFSETQuelleEREIGNISSESENSORIKEREIGNISLIEST
  • Ontologie (je Objekt geordnet): Aussage.
  • Verarbeitung (mit Zustand): Fenster, Zustand.
  • Log (Partition je Schlüssel): Partition, Offset.
  • Quelle (Ereignisse): Sensorik, Ereignis.
  • liest: fließt von quelle nach ontologie.
SCHNITT VON VORN

Doppel erkennen

Zustellung mindestens einmal heißt: Ein Ereignis kann mehrfach ankommen, weil ein Sender nach einem Timeout wiederholt oder weil FORGE nach einem Neustart ab dem zuletzt festgeschriebenen Offset liest. FORGE setzt darum jedes Ereignis idempotent um. Jedes Ereignis trägt eine Kennung und je Schlüssel eine fortlaufende Nummer oder die Logposition der Quelle. Die Ontologie nimmt es nur an, wenn die Nummer größer ist als die zuletzt angewandte, und verwirft es sonst.

Kafka kennt eigene Garantien: idempotente Sender und Transaktionen, mit denen sich Lesen, Verarbeiten und Schreiben zwischen Themen von Kafka genau einmal vollziehen lässt, wenn die Leser nur festgeschriebene Nachrichten lesen (read_committed). Diese Garantie endet an der Grenze von Kafka. Ein Schreiben in eine andere Datenbank ist nicht Teil der Kafka-Transaktion. FORGE sichert den letzten Schritt deshalb selbst: Die Nummer des Ereignisses steht in derselben Datenbanktransaktion wie die Änderung in der Ontologie.

Den Offset schreibt FORGE erst fest, nachdem die Änderung bestätigt ist. Fällt es dazwischen aus, liest es dieselben Ereignisse erneut, und die Nummern verwerfen sie.

Ereignis = ( kennung, schluessel, nummer,
             ereigniszeit, aufnahmezeit, nutzlast )

anwenden(e):
  nummer ≤ angewandt(schluessel)       verwerfen         Doppel oder Nachzügler
  nummer = angewandt(schluessel) + 1   anwenden           Änderung und Nummer in einer Transaktion
  sonst                                zurückstellen     Lücke: auf den Vorgänger warten
Anwenden eines Ereignisses, schematisch. Die Lückenprüfung gilt nur, wo die Quelle eine lückenlose Nummer je Schlüssel liefert.

Ein Ereignis, drei Ausgänge

Ein Ereignis wird geprüft, gegen die Nummer gehalten und mit ihr geschrieben. Ein Doppel endet im Verwerfen, ein nicht lesbares Ereignis in der Fehlerspur. Der Offset rückt erst nach dem Schreiben.

Ereignis in FORGE: Prüfen, Nummer, Schreiben, Bestätigung, Verwerfen oder FehlerspurEin Ereignis wird geprüft, gegen die Nummer gehalten und mit ihr geschrieben. Ein Doppel endet im Verwerfen, ein nicht lesbares Ereignis in der Fehlerspur. Der Offset rückt erst nach dem Schreiben.nicht lesbarDoppelEREIGNISAUSSAGEPRÜFENNUMMERSCHREIBENOFFSETFEHLERSPURVERWERFEN
  • Ablauf von Ereignis bis Aussage: Prüfen, Nummer, Schreiben, Offset, Fehlerspur, Verwerfen.
  • Prüfen zu Nummer.
  • Nummer zu Schreiben.
  • Schreiben zu Offset.
  • Prüfen nicht lesbar Fehlerspur.
  • Nummer Doppel Verwerfen.
ABLAUF · WEGE OHNE MENGEN

Verspätete Ereignisse

Auch in einem geordneten Strom kommt ein Ereignis manchmal erst an, nachdem spätere schon verarbeitet sind: Ein Gerät sendet nach einem Funkloch gesammelt nach, ein Vorsystem schreibt mit Verzug. Fenster über der Ereigniszeit schließen darum nicht mit der Uhr, sondern anhand eines Wasserzeichens, der Schätzung, bis zu welcher Ereigniszeit alles da ist. Das Konzept stammt aus der Forschung zur Stromverarbeitung und ist in Werkzeugen wie Apache Flink üblich.

Ein Ereignis, das nach dem Wasserzeichen kommt, wird nicht verworfen. Innerhalb einer erlaubten Verspätung korrigiert es das Fenster, das es betrifft. Danach läuft es als Korrektur in die Ontologie: Die alte Aussage schließt ihre Erfassungszeit, und eine neue mit derselben Gültigkeitszeit tritt an ihre Stelle. Eine frühere Auswertung bleibt so reproduzierbar, und die Verspätung ist als solche sichtbar.

Abgeleitete Werte, etwa Summen oder Zählungen über ein Fenster, werden nach einer Korrektur neu berechnet und mit neuer Erfassungszeit abgelegt. Wie lang die erlaubte Verspätung ist, steht je Strom an der Abbildung und nicht im Code.

Zustand und Wiederanlauf

Verarbeitung mit Gedächtnis, etwa das Zählen in einem Fenster oder das Zusammenführen zweier Ströme, hält Zustand. Er wird regelmäßig als Prüfpunkt gesichert, zusammen mit den Offsets, bis zu denen die Eingaben dafür gelesen sind. Das Verfahren, dafür die Ströme mit Markierungen zu durchlaufen, geht auf den Algorithmus von Chandy und Lamport für verteilte Schnappschüsse zurück.

Nach einem Ausfall stellt FORGE den letzten Prüfpunkt her und liest ab den gesicherten Offsets neu. Weil die Verarbeitung deterministisch und das Ende idempotent ist, ergibt dieselbe Strecke dasselbe Ergebnis. Dieselbe Eigenschaft erlaubt es, eine fehlerhafte Verarbeitung zu korrigieren und den Strom ab einem früheren Offset erneut laufen zu lassen.

Die Aufbewahrung des Logs ist begrenzt. Was weiter zurückreicht, als der Strom hält, liegt nur in der Rohzone, in die jedes Ereignis unverändert und anhängend geschrieben wird, bevor es weiterläuft. Der Strom ist der schnelle Weg, die Rohzone die Quelle für Wiederholungen.

Rückstau und nicht lesbare Ereignisse

Kommt ein Strom schneller an, als FORGE verarbeitet, wächst der Rückstand im Log, der Abstand zwischen dem letzten Eintrag und dem gelesenen Offset (Consumer Lag). FORGE misst ihn je Strom und Partition und meldet bei einer Schwelle. Puffer im Speicher bleiben begrenzt, damit ein Stau nicht als Speicherfehler endet: Das Log nimmt den Stau auf, und die Leser holen auf, bis ihre Zahl die der Partitionen erreicht.

Ein Ereignis, das nicht dem Schema entspricht oder dessen Nutzlast nicht gelesen werden kann, darf den Strom nicht anhalten. Es geht mit Grund, Fundstelle und den Originalbytes in eine Fehlerspur (Dead-Letter-Topic) und wird dort geprüft. Weil damit die Ordnung dieses Schlüssels reißt, markiert FORGE das Objekt als offen, bis das Ereignis geklärt und nachgeholt ist. Wo ein Objekt strikte Ordnung braucht, hält stattdessen die Partition an; das ist je Strom eine bewusste Einstellung.

Das Schema der Nutzlast steht in einem Schema-Register, mit Avro, JSON Schema oder Protobuf. Eine Änderung muss mit der Kompatibilitätsregel des Stroms verträglich sein, bevor ein Sender sie verwendet.

Eckdaten

Log
Apache Kafka; Partition je Schlüssel, Offset je Partition
Ordnung
je Schlüssel; der Schlüssel ist die Identität des Objekts
Zustellung
mindestens einmal; Wirkung genau einmal durch die Nummer je Schlüssel
Kafka-Transaktionen
genau einmal innerhalb von Kafka, Leser mit read_committed
Zeit
Ereigniszeit wird Gültigkeitszeit, Aufnahmezeit wird Erfassungszeit
Verspätung
Wasserzeichen, erlaubte Verspätung je Strom, danach Korrektur mit neuer Erfassungszeit
Wiederanlauf
Prüfpunkt mit Offsets; für längere Strecken die Rohzone
Fehler
Fehlerspur (Dead-Letter-Topic); Rückstand als Größe mit Schwelle
Schema
Schema-Register mit Avro, JSON Schema, Protobuf; Kompatibilitätsregel je Strom

Weiter

Welcher Ihrer Ströme darf nie zweimal wirken? An ihm legen wir mit Ihnen Schlüssel und Nummer fest.