Apache Avro : le contrat entre modules

Deux ingénieurs vus de dos à une table partagée dans un bureau calme, un grand écran mural entre eux montrant un schéma abstrait bleu et gris d'un formulaire et de ses versions, une feuille imprimée sous une main, un seul voyant bleu sur le cadre de l'écran

L’équipe commerce ajoute un pourcentage de remise à la commande. C’est un petit changement, convenu en réunion, et le module qui crée les commandes commence à l’envoyer l’après-midi même. Le module finance, qui lit les commandes depuis deux ans, ne connaît pas le champ et cesse d’accepter le message. Personne n’avait l’intention de changer un contrat. Il y avait un contrat, simplement il n’était écrit nulle part ailleurs que dans deux morceaux de code qui, jusque-là, étaient d’accord.

Dans un système fait de modules qui échangent des événements, chaque événement est un accord. Apache Avro est l’un des formats qui inscrivent cet accord dans les données elles-mêmes, et s’il figure dans la conception de la couche Données de Hurukan, qui est en construction, c’est moins une affaire d’octets que de ce qui se passe quand l’accord doit changer.

Le schéma voyage avec les données

Un enregistrement Avro est une ligne binaire compacte. Seul, il est illisible : les noms des champs, les types et l’ordre ne sont pas dans les octets. Ils sont dans le schéma, écrit en JSON, et la spécification définit un petit ensemble de types pour l’écrire : null, boolean, int, long, float, double, bytes, string, et les types complexes record, enum, array, map, union et fixed. Un champ peut porter une valeur par défaut, et c’est cette valeur qui, plus tard, rend le changement supportable. La version courante est 1.12.2 (blog du projet, vérifié le 21 octobre 2026).

Le schéma atteint le lecteur de deux manières. Dans un fichier, le format Object Container File met le schéma dans l’en-tête, suivi de blocs d’enregistrements séparés par un marqueur de synchronisation, de sorte qu’un fichier retrouvé des années plus tard s’explique encore lui-même. Sur un bus de messages, où envoyer le schéma avec chaque événement serait du gaspillage, l’encodage à objet unique de la spécification place un marqueur de deux octets et une empreinte de huit octets du schéma devant chaque enregistrement ; un registre conserve le schéma sous cette empreinte. Le registre Confluent, largement utilisé, fait de même avec son propre cadrage, un octet magique et un identifiant de schéma sur quatre octets devant les octets Avro (documentation Confluent).

Dans les deux cas, le lecteur ne devine jamais. Il a le schéma de l’écrivain et le sien, et le reste est affaire de règles.

Résolution : comment un lecteur rencontre un écrivain qu’il n’a jamais vu

La spécification fixe ce qui se passe quand le schéma de l’écrivain et celui du lecteur diffèrent. Les champs sont appariés par nom. Un champ envoyé par l’écrivain et absent chez le lecteur est ignoré. Un champ attendu par le lecteur et non envoyé par l’écrivain est rempli à partir de la valeur par défaut du lecteur, et s’il n’y a pas de valeur par défaut, la lecture échoue avec une raison claire. Certains types peuvent être promus au passage : un int peut être lu comme un long, un long comme un float ou un double, un float comme un double, et string et bytes peuvent être lus l’un comme l’autre. Une union est appariée à la première branche compatible du côté lecteur.

Écrites noir sur blanc, ces règles sont toute la discipline du champ remise. Ajoutez-le avec une valeur par défaut et le module finance continue de lire, en ignorant le champ jusqu’à ce qu’on lui apprenne à l’utiliser. Ajoutez-le sans valeur par défaut et retirez un ancien champ dans le même changement, et le lecteur le plus ancien de l’entreprise s’arrête. Les règles ne sont pas des conseils ; elles décident de la version de la matinée que vous aurez.

Un changement de schéma comme proposition

Dans Hurukan, la conception veut qu’un schéma d’événement soit un objet de l’ontologie comme un autre : il a un propriétaire, des versions et une place dans le registre. Cela transforme le champ remise, d’une modification de code d’un après-midi, en un court chemin enregistré.

Un agent propose. Le module commerce a besoin d’un nouveau champ ; un agent rédige le changement de schéma, avec la valeur par défaut, le type et la raison, et nomme les modules qui lisent cet événement.

Le registre vérifie. La version proposée est testée contre les schémas des lecteurs selon les règles de résolution ci-dessus. Un changement qui arrêterait un lecteur existant est rejeté avant que quiconque le voie, avec le nom du lecteur.

Le propriétaire approuve. Un changement compatible reste un changement d’un accord entre modules. Le propriétaire de l’événement, une personne, l’approuve, et alors seulement les producteurs commencent à écrire la nouvelle version.

Le registre le conserve. L’approbation, la version et l’heure sont écrites dans le registre, de sorte qu’un an plus tard la question « quand les remises sont-elles apparues sur les commandes, et qui l’a permis » a une réponse. C’est le même chemin que suit chaque changement du système : un agent propose, une personne décide, le système enregistre. L’idée d’ensemble est dans l’état courant et le registre.

Ce pour quoi Avro n’est pas fait

Avro transporte bien les événements et lit bien des enregistrements entiers. C’est une mauvaise disposition pour la question que la superintelligence pose le plus souvent, quelques champs sur un million de lignes, parce que chaque champ est sur le chemin de tous les autres. C’est pourquoi, dans la conception, les événements en Avro sont regroupés en colonnes une fois acceptés. Quelle forme va où est exposé dans Parquet et Avro : l’événement et le registre.

Avro apparaît aussi à un endroit où peu de gens regardent : le format de table Apache Iceberg écrit ses manifestes, les listes des fichiers de données qui composent une table, sous forme de fichiers Avro (spécification Iceberg). Le format en lignes qui transporte les événements entre modules transporte aussi la comptabilité du registre.

Ce qui reste aux personnes

Un agent peut rédiger un schéma, lancer le contrôle de compatibilité et nommer les lecteurs. Il ne peut pas décider qu’un champ sur lequel le module finance comptait peut disparaître, qu’une valeur par défaut de zéro est une affirmation vraie sur les anciennes commandes, ou qu’un lecteur peut être autorisé à échouer. Ce sont des décisions sur le sens du métier, et dans Hurukan elles sont prises par le propriétaire de l’événement et consignées.

Un test à faire aujourd’hui

Choisissez un message qui circule entre deux de vos systèmes. Trouvez où ses champs sont définis. Si la réponse est « dans le code émetteur » et « dans le code récepteur », vous avez deux définitions et aucun contrat. Demandez ensuite qui approuverait l’ajout d’un champ, nommément. S’il n’y a pas de nom, le prochain champ sera ajouté par le premier qui en aura besoin, et découvert par celui qu’il cassera. La page couche Données décrit où ce contrat est censé vivre dans Hurukan ; la présentation montre les modules autour.