xmlstreamer-ng
Streamer de feeds XML con aislamiento de fallos construido sobre rypipe, adaptado de carlosplanchon/xmlstreamer. Memoria acotada, recuperación conservadora y fallos parciales explícitos.
Una adaptación de carlosplanchon/xmlstreamer basada en rypipe que transmite feeds XML grandes o dañados a registros Python sin ocultar elementos corruptos.
Cargando últimos commits…
Rendimiento paralelo
15,8M filas/s
Throughput
2,12 GB/s
Paralelo acotado
10,2M filas/s
Pico RSS (acotado)
202 MB
Problema
Los feeds XML de gran tamaño procedentes de sistemas remotos suelen tener formato incorrecto, estar mal codificados o verse interrumpidos en tránsito. Un analizador estricto pierde todo el documento por un disco rayado; un analizador de recuperación agresivo puede reescribir silenciosamente marcas dañadas en registros que nunca existieron.
xmlstreamer-ng está diseñado para el espacio entre esos dos comportamientos: entregar cada elemento intacto antes y después de la corrupción localizada, nunca inventar registros e informar lo que se eliminó en lugar de presentar el procesamiento incompleto como un éxito.
Origen
xmlstreamer-ng está basado en carlosplanchon/xmlstreamer de Carlos A. Planchón (MIT) y conserva su enfoque de streaming por ítems para feeds XML grandes o dañados. El motor de parseo se construye sobre rypipe (rypipe-core), que aporta el tokenizador de bytes, el splitter, el parseo por ítem y la ejecución Arrow.
¿Por qué es difícil?
La recuperación tiene que ser conservadora sin volverse inútil. Las etiquetas separadoras deben reconocerse dentro de los comentarios, CDATA, instrucciones de procesamiento y DOCTYPE sin que nunca las inventen. La detección de codificación debe superar una declaración XML mentirosa sin corromper el UTF-8 válido. La memoria limitada debe contener mientras que los elementos, las secciones de marcado y los descriptores puedan ser cada uno más grandes que el búfer de lectura.
La ruta de columnas agrega un segundo contrato: el mismo motor Rust tiene que producir lotes Arrow bajo proyección, filtrado y ejecución en paralelo, con una semántica de caída de elementos mal formados idéntica a la ruta de transmisión.
Rendimiento
Últimos benchmarks en un Ryzen 7 5800X sobre un feed de 128 MiB con 951.898 ítems: XmlSource en paralelo (8 hilos) alcanza 15,8M filas/s y 2,12 GB/s; el streaming paralelo acotado alcanza 10,2M filas/s y 1,36 GB/s con 202 MB de RSS máximo.
Resultados del benchmark
| Motor | Modo | items/s | MB/s | RSS máxima |
|---|---|---|---|---|
| xmlstreamer-ng | XmlSource paralelo (8 hilos) | 15,8M | 2122,6 | 283 MB |
| xmlstreamer-ng | Paralelo acotado (8 hilos) | 10,2M | 1364,7 | 202 MB |
| xmlstreamer-ng | XmlSource columnar | 4,6M | 621,6 | 297 MB |
| quick-xml (nativo) | solo parseo y conteo | 3,2M | 433,0 | 14 MB |
| lxml iterparse | dicts por ítem | 304k | 40,8 | 26 MB |
| xmlstreamer (original) | dicts por ítem | 103k | 13,8 | 34 MB |
Feed sintético de 128 MiB, 951.898 ítems, mediana de 5 ejecuciones en un Ryzen 7 5800X. Las filas nativas solo parsean y cuentan; no construyen registros.
Decisiones clave
Dos API, un motor
StreamInterpreter mantiene el contrato de recuperación por artículo; XmlSource expone la proyección, el filtrado y los lotes paralelos de Arrow desde el mismo analizador Rust.
Contabilidad explícita de fallos
Los elementos mal formados se registran y cuentan, nunca se reparan silenciosamente. Las estadísticas finales distinguen el daño del feed de los elementos eliminados por el filtro de la persona que llama.
Recuperación conservadora
La corrupción contenida dentro de un objeto solo elimina ese objeto. Las secciones que nunca se cierran son una elección documentada a través de Secciones(on_limit=...), anunciadas con una advertencia.
Garantías
Compensaciones
- La etiqueta separadora no debe encajarse dentro de sí misma.
- Los elementos se crean a partir del texto del elemento, no de los atributos XML
- lxml.iterparse puede servir mejor a los comentarios no confiables y bien formados
- API por elemento es más lento que el adaptador columnar/paralelo