Schema Management in Kafka
6.05.2024Anatoly Zelenin
IT-Trainer und Apache Kafka Experte
In meiner Apache Kafka Mittagsakademie können Teilnehmerinnen und Teilnehmer meiner Kafka Kurse weitere interessante Themen kennenlernen, auch wenn die Schulung eigentlich schon vorbei ist. Dieser Blogpost ist ein bearbeitetes Transkript des Videos. Die praktischen Übungen stehen exklusiv den Teilnehmenden zur Verfügung. Wenn du dich auch darin ausprobieren möchtest, dann schreib mir gerne.
Daten haben immer ein Schema. Die Frage ist nur, ob du und die anderen Entwicklungsteams davon wissen, ob die Schemas implizit irgendwo im Code gespeichert sind und in JSON-Strings abgelegt oder explizit aufgeschrieben, abgestimmt und mit den anderen Teams geteilt.
Schemas vs. Datenformate
Wichtig ist, dass Schemas nicht das Gleiche sind wie Datenformate. Die Diskussion rund um Schemas ist unabhängig vom eingesetzten Datenformat und auch meistens unabhängig von der eingesetzten Technologie. Egal, ob JSON, Avro, Protobuf oder ein eigenes binäres Protokoll: Ohne Schema geht es nicht. In relationalen Datenbanken ist das Thema Schemas relativ einfach: alle Services, die mit der Datenbank interagieren, müssen mit demselben Schema klarkommen, nämlich der aktuellen Datenbankstruktur.
Schema Evolution
Wie wir wissen, sind Migrationen alles andere als trivial. Insbesondere, wenn sie die Kompatibilität brechen. Bei Kafka kommt erschwerend hinzu, dass es ein Log ist und Daten längere Zeit in Kafka sein können. Das heißt, es kann passieren und oft wird es auch so passieren, dass wir in einem Topic unterschiedliche Schema Versionen haben. Es kann sogar vorkommen, dass es eine Zeit gibt, in der unterschiedliche Producer unterschiedliches Schema Versionen schreiben.
Das ist per se nichts Schlimmes, aber wir müssen uns vorher Gedanken machen, wie unsere Consumer damit umgehen sollen. Und so kommen wir zu einem der wichtigsten Themen in Kafkas Schema Welt: Der Schema-Evolution, denn Daten werden sich ganz bestimmt verändern und die Frage ist, wie wir damit umgehen.
Update-Reihenfolgen
Kompatibilität | Consumer & Daten | Update-Strategie |
---|---|---|
Vorwärts | Alte Consumer – Neue Daten | 1. Producer, 2. Consumer |
Rückwärts | Neue Consumer – Alte Daten | 1. Consume, 2. Producer |
Beidseitig | Alte/Neue Consumer – Alte/Neue Daten | Egal |
Keine | Consumer = Daten | Gleichzeitig |
Zunächst sollten wir uns Gedanken über die Kompatibilität machen. Sollen unsere Consumer vorwärts kompatibel sein, das bedeutet das alte Consumer auch neuere Daten lesen können? Oder benötigen wir Rückwärts-Kompatibilität, dann müssen aktuelle Consumer in der Lage sein, alte Daten zu lesen, oder müssen wir gar in beide Richtungen kompatibel sein, dann ist es egal, in welche Richtung wir schauen: Jeder Consumer muss mit allen Daten umgehen können. Manchmal ist es auch okay, zu sagen, es gibt keine Kompatibilität. Dann gehen Dinge schief, wenn die Consumer nicht zur Version passende Daten erhalten. Zusätzlich müssen wir uns Gedanken über unsere Update-Strategien machen. Wenn wir keine Kompatibilitäten benötigen, dann müssen wir Consumer und Producer gleichzeitig aktualisieren. Das erreichen wir zum Beispiel, indem wir die Producer stoppen, dann die Consumer alle Daten verarbeiten lassen, dann die Producer in einer neuen Version deployen und die Consumer auch gleichzeitig in einer neuen Version deployen. Bei Vorwärts-Kompatibilität werden zuerst die Producer upgedatet und dann die Consumer. Bei Rückwärts-Kompatibilität genau umgekehrt: erst Consumer dann Producer. Und wenn wir in beide Richtungen kompatibel sein wollen, ist es egal, was zuerst upgedatet wird.
Ein weiterer Punkt, auf den wir achten sollten, betrifft die Transitivität: reicht es uns aus, wenn der Consumer nur die letzte Version lesen kann oder benötigen wir den Consumer so, dass er alle vorherigen Version des Datums lesen kann. Dafür muss der Transitiv rückwärts kompatibel sein.
Kompatibilität
Aber lass es uns mal anhand eines Beispiels machen: Nehmen wir die drei Kompatibilitäten vorwärts, rückwärts und in beide Richtungen. Und nun führen wir eine der folgenden vier Operationen durch: Wir fügen ein verpflichtendes Feld hinzu oder löschen ein Pflichtfeld. Wir fügen ein optionales Feld hinzu oder löschen ein optionales Feld.
Schauen wir uns das mal an: Pflichtfelder löschen können wir, wenn wir rückwärts kompatibel sind, aber nicht vorwärts kompatibel. Pflichtfelder hinzufügen nur bei Vorwärts-Kompatibilität und optionale Felder dürfen wir immer hinzufügen oder entfernen, ohne die Kompatibilität einzuschränken. Wenn wir Schema Registries in Kafka benutzen, können wir die gewünschte Kompatibilität einstellen.
Schema Registries
Egal, ob ihr am Ende eine Schema Registry benutzt oder nicht, hier eine Empfehlung, die ich meinen Beratungskunden gebe: Ihr benötigt einen zentralen Ort, an dem ihr Schemas dokumentiert, verwaltet und teilt. Wie genau ihr das tut, das hängt von euren Gegebenheiten ab. Natürlich kann das eine Seite im Confluence sein oder in einem anderen Wiki. Aber meist kommt ihr besser, wenn in die Schemas in Git verwaltet und dann eine Schema Registry benutzt. Meine Meinung ist, dass es am besten einen Git basierten Prozess geben sollte, um die Governance zu gewährleisten und die Schemas dann per CI/CD Pipeline in die Schema Registry deployt werden. Auf keinen Fall sollte es so sein, wie es der Standard ist: per Code in eurem Java Code in die Produktion deployen.
Vor allem, wenn wir Avro benutzen, ist der Einsatz einer Schema Registry sehr sinnvoll. Anstatt das Schema mit jeder Nachricht mitzuschicken, legen wir in der Schema Registry das Schema ab und der Producer schreibt nur die ID des Schemas in die Nachricht und reduziert so den Ballast drastisch, aber auch bei anderen Datenformaten ist die sinnvoll. Bei JSON-Schema, Protobuf und Co.
Unterschiedliche Schema Registries
Mir sind drei Schema Registries bekannt. Die wohl bekannteste ist die Confluent Schema Registry, das ist auch das Original, und wenn ihr ohnehin Confluent einsetzt, dann ist das die sinnvollste Wahl. Falls ihr nach einer Schema Registry mit richtiger Open-Source-Lizenz Ausschau haltet, ist Aivens Karapace eine gute Wahl. Diese ist API kompatibel mit der Registry von Confluent. Und wir setzen sie auch im Lab ein. Eine weitere sehr spannende Schema Registry ist Apicurio. Apicurio unterstützt nicht nur Kafka, sondern auch unterschiedliche API-Schema Formate wie Async API, Open API und Co. Das in der Tiefe zu betrachten, würde aber dieses Modul sprengen.
Viel Spaß beim Ausprobieren!
Anatoly Zelenin vermittelt als IT-Trainer hunderten Teilnehmern Apache Kafka in interaktiven Schulungen. Seine Kunden aus dem DAX-Umfeld und dem deutschen Mittelstand schätzen seit über einem Jahrzehnt seine Expertise und seine begeisternde Art. Darüber hinaus ist er nicht nur IT-Berater und -Trainer, sondern erkundet auch als Abenteurer unseren Planeten.