xmlstreamer-ng
Streamer de flux XML à isolation des défauts construit sur rypipe, adapté de carlosplanchon/xmlstreamer. Mémoire bornée, récupération conservatrice et échecs partiels explicites.
Une adaptation de carlosplanchon/xmlstreamer basée sur rypipe qui diffuse des flux XML volumineux ou endommagés vers des enregistrements Python sans masquer les éléments corrompus.
Chargement des derniers commits…
Débit parallèle
15,8M lignes/s
Débit
2,12 Go/s
Parallèle borné
10,2M lignes/s
Pic RSS (borné)
202 Mo
Problème
Les flux XML volumineux provenant de systèmes distants sont souvent mal formés, mal codés ou interrompus en cours de transit. Un analyseur strict perd l'intégralité du document au profit d'un seul enregistrement brisé ; un analyseur de récupération agressif peut réécrire silencieusement le balisage endommagé dans des enregistrements qui n'ont jamais existé.
xmlstreamer-ng est conçu pour l'espace entre ces deux comportements : livrer chaque élément intact avant et après une corruption localisée, ne jamais inventer d'enregistrements et signaler ce qui a été supprimé au lieu de présenter un traitement incomplet comme un succès.
Origine
xmlstreamer-ng est basé sur carlosplanchon/xmlstreamer de Carlos A. Planchón (MIT) et conserve son approche de streaming par élément pour les flux XML volumineux ou endommagés. Le moteur d'analyse repose sur rypipe (rypipe-core), qui fournit le tokenizer d'octets, le splitter, l'analyse par élément et l'exécution Arrow.
Pourquoi c'est dur
La reprise doit être conservatrice sans devenir inutile. Les balises séparatrices doivent être reconnues à l'intérieur des commentaires, CDATA, des instructions de traitement et DOCTYPE sans jamais être inventées par eux. La détection de codage doit vaincre une déclaration XML mensongère sans corrompre un UTF-8 valide. La mémoire limitée doit être conservée tandis que les éléments, les sections de balisage et les descripteurs peuvent chacun être plus grands que le tampon de lecture.
Le chemin en colonnes ajoute un deuxième contrat : le même moteur Rust doit produire des lots Arrow sous projection, filtrage et exécution parallèle, avec une sémantique de suppression d'éléments mal formés identique au chemin de streaming.
Débit
Derniers benchmarks sur un Ryzen 7 5800X avec un flux de 128 Mio et 951 898 éléments : XmlSource en parallèle (8 threads) atteint 15,8M lignes/s et 2,12 Go/s ; le streaming parallèle borné atteint 10,2M lignes/s et 1,36 Go/s avec 202 Mo de RSS maximal.
Résultats du benchmark
| Moteur | Mode | items/s | Mo/s | RSS max |
|---|---|---|---|---|
| xmlstreamer-ng | XmlSource parallèle (8 threads) | 15,8M | 2122,6 | 283 Mo |
| xmlstreamer-ng | Parallèle borné (8 threads) | 10,2M | 1364,7 | 202 Mo |
| xmlstreamer-ng | XmlSource colonnaire | 4,6M | 621,6 | 297 Mo |
| quick-xml (natif) | parse + comptage seulement | 3,2M | 433,0 | 14 Mo |
| lxml iterparse | dicts par élément | 304k | 40,8 | 26 Mo |
| xmlstreamer (original) | dicts par élément | 103k | 13,8 | 34 Mo |
Flux synthétique de 128 Mio, 951 898 éléments, médiane de 5 exécutions sur un Ryzen 7 5800X. Les lignes natives ne font que parser et compter ; elles ne construisent pas d'enregistrements.
Décisions clés
Deux API, un moteur
StreamInterpreter conserve le contrat de récupération par article ; XmlSource expose la projection, le filtrage et les lots parallèles Arrow à partir du même analyseur Rust.
Comptabilisation des échecs explicites
Les éléments mal formés sont enregistrés et comptés, jamais réparés en silence. Les statistiques de clôture distinguent les dommages causés au flux des éléments supprimés par le filtre de l'appelant.
Reprise conservatrice
La corruption contenue à l'intérieur d'un objet ne laisse tomber que cet objet. Les sections qui ne se ferment jamais sont un choix documenté via Sections(on_limit=...), annoncé avec un avertissement.
Garanties
Compromis
- La balise de séparation ne doit pas s'emboîter à l'intérieur d'elle-même
- Les éléments sont construits à partir du texte de l'élément et non des attributs XML.
- Une entrée non fiable et bien formée peut être mieux servie par lxml.iterparse
- Le API par article est plus lent que l'adaptateur en colonne/parallèle