Be Software Architecture Wise
Be Wise Apps

Read in English · Leer en español · Auf Deutsch lesen

Section 5.4 sur 5.8 · Intermédiaire · 23 min de lecture

Tubes et filtres, et architectures en pipeline

Leçon vidéo

5 min · narré en anglais, avec sous-titres
Écouter seulement (audio)
Lire la transcription

Certains problèmes sont naturellement un flux : les données entrent, passent par une série d’étapes et ressortent. Dans cette leçon, vous allez découvrir le style conçu pour cela, ce qui limite sa vitesse, et où il est faible.

Pensez à une station de lavage automatique. Chaque voiture passe par une série de postes : trempage, brossage, rinçage, séchage. Chaque poste fait un seul travail, ne sait rien des autres et fait passer la voiture au suivant sur un tapis roulant. Vous pouvez ajouter un poste de cirage sans modifier les autres. Et si le séchage est lent, les voitures commencent à faire la queue devant lui. Le style tubes et filtres fonctionne de la même façon pour les données : les postes sont les filtres, et le tapis roulant est le tube.

Voici un pipeline de la plateforme de livraison de repas. Un producteur, ou source, démarre le flux : il lit les événements de commande. Un transformateur modifie chaque enregistrement. Celui-ci ajoute le restaurant et la région. Un testeur vérifie une condition et transmet chaque enregistrement ou l’écarte. Ici, il écarte les commandes de test. Et un consommateur, ou puits, termine le flux en écrivant dans l’entrepôt de données. Entre les filtres, des tubes comme celui-ci mettent les enregistrements en tampon et les transportent dans un seul sens. Les contraintes clés : les filtres ne partagent aucun état, et chacun ne connaît que le format de ce qu’il lit et écrit, jamais ses voisins.

Ces contraintes produisent directement les avantages. Comme un filtre ne partage aucun état, il peut être écrit et testé isolément, avec une liste d’enregistrements en entrée et les sorties que vous attendez. Il peut être réutilisé dans d’autres pipelines, et les filtres peuvent être ajoutés, retirés ou réordonnés simplement en modifiant la façon dont le pipeline est assemblé. Et comme les enregistrements circulent un par un, un filtre peut commencer le premier enregistrement avant que le précédent ait fini le dernier. Les étapes s’exécutent de façon concurrente et n’ont besoin de mémoire que pour quelques enregistrements.

Le terrain d’élection le plus connu du style est Unix. En 1964, Doug McIlroy écrivait que les programmes devraient être raccordés comme un tuyau d’arrosage. Ken Thompson a ajouté les tubes à Unix en 1973, et l’idée de petits outils reliés par des tubes a rendu le style célèbre. Aujourd’hui, c’est le choix par défaut en ingénierie des données. Les traitements ETL, extraire, transformer, charger, déplacent les commandes de la veille vers un entrepôt de données, une étape à la fois. Un compilateur classique est lui aussi un pipeline : les caractères deviennent des lexèmes, les lexèmes deviennent un arbre syntaxique, puis viennent les vérifications, l’optimisation et le code machine. Le traitement multimédia et celui des journaux fonctionnent de la même façon. Et une même structure peut s’exécuter en batch sur une entrée bornée, ou en flux qui ne se termine jamais.

Passons à un exemple commenté. La plateforme charge ses événements de commande dans son entrepôt de données au moyen de quatre filtres. Sur un seul worker, Parse traite cinquante mille enregistrements par seconde, Enrich huit mille, Validate quarante mille et Load douze mille. Un pipeline ne peut pas aller plus vite que son filtre le plus lent, donc celui-ci tourne à huit mille enregistrements par seconde. Un jour de forte activité, il y a un million d’événements de commande. Cela prend cent vingt-cinq secondes. Enrich ne conserve aucun état, donc vous pouvez en exécuter deux copies en parallèle, chacune prenant la moitié des enregistrements. Cette étape traite désormais seize mille enregistrements par seconde, et Load, à douze mille, devient le goulet d’étranglement. Les données de la journée prennent maintenant environ quatre-vingt-trois secondes : un gain de trente-trois pour cent, et non des cinquante pour cent qu’une lecture naïve laisse supposer. Accélérer autre chose que le goulet d’étranglement ne change rien, alors mesurez d’abord.

Les écarts de vitesse posent un second problème. Sans contrôle, Parse pousserait cinquante mille enregistrements par seconde vers une étape Enrich qui n’en écoule que huit mille. Le tampon grossirait de quarante-deux mille enregistrements chaque seconde, jusqu’à épuisement de la mémoire. La réponse est le back pressure : obliger l’étape rapide à ralentir jusqu’à la vitesse de l’étape lente. Un tube Unix le fait automatiquement, car un tube plein bloque l’écrivain. Les erreurs exigent une politique pour chaque filtre. Un enregistrement malformé ne doit pas arrêter le pipeline : envoyez-le dans une file de lettres mortes avec le motif. Et comme les redémarrages impliquent que certains enregistrements soient traités deux fois, les puits doivent être idempotents.

Utilisez les tubes et filtres quand le problème est naturellement un flux de données à sens unique : ingestion, conversion, enrichissement, validation et export. Le style apparaît aussi à l’intérieur d’autres styles, comme un traitement de reporting nocturne dans un monolithe modulaire. Évitez-le comme structure principale des applications interactives, où les utilisateurs attendent des réponses immédiates, et pour une logique où de nombreuses étapes partagent et modifient le même état. Et comme les filtres s’ignorent délibérément les uns les autres, une défaillance découverte à l’étape quatre ne peut pas facilement annuler l’étape deux. Si vous avez besoin d’un retour arrière coordonné, il vous faut probablement un autre style.

La section complète compare le batch et le streaming, montre un pipeline sous Unix et en Python, et évalue les caractéristiques du style. Ensuite, dans la section cinq point cinq, nous examinerons les architectures micronoyau et à plug-ins. À très vite.

Ce qu’il faut savoir

Définition et origine

Dans le style tubes et filtres (pipes and filters), le traitement est une chaîne d’étapes indépendantes. Chaque filtre lit des données sur son entrée, effectue un seul travail sur elles et écrit le résultat sur sa sortie. Chaque tube (pipe) transporte les données dans un seul sens d’un filtre au suivant, généralement avec un tampon entre les deux. La description de Garlan et Shaw (1994) énonce les contraintes qui donnent au style ses propriétés : les filtres doivent être indépendants, ne doivent pas partager d’état avec d’autres filtres et ne doivent pas connaître l’identité des filtres situés à chaque extrémité de leurs tubes. Un filtre ne connaît que le format de ce qu’il lit et de ce qu’il écrit.

Le terrain d’élection le plus connu du style est Unix. Dans une note datée du 11 octobre 1964, Doug McIlroy écrivait qu’il devrait exister des moyens de raccorder les programmes comme un tuyau d’arrosage, en vissant un autre segment quand les données doivent être retravaillées d’une autre manière. Ken Thompson a implémenté l’appel système pipe et les tubes dans le shell de la Version 3 d’Unix en 1973, et la notation | a suivi dans la Version 4. La philosophie Unix qui en est née, écrire des programmes qui font une seule chose et la font bien, et qui collaborent par une interface texte universelle, est le style tubes et filtres appliqué à l’ensemble des outils d’un système d’exploitation. POSA (1996) a catalogué Pipes and Filters comme patron architectural, et Enterprise Integration Patterns (2003) de Hohpe et Woolf l’a appliqué à la messagerie entre systèmes.

Richards et Ford appellent ce même style l’architecture en pipeline, et nomment quatre sortes de filtres. Un producteur (ou source) démarre le flux, en lisant dans un fichier, une base de données ou une file. Un transformateur modifie chaque enregistrement : analyse syntaxique, conversion, enrichissement. Un testeur vérifie une condition et transmet l’enregistrement ou l’écarte, parfois en l’aiguillant vers l’une de plusieurs sorties. Un consommateur (ou puits) termine le flux, en écrivant dans une base de données, un fichier ou sur un écran.

Filtres et tubes

Pipeline de quatre filtres et trois tubes à sens unique : un producteur lit les événements de commande, un transformateur ajoute restaurant et région, un testeur écarte les commandes test, un consommateur écrit dans l’entrepôtProducteurlit les évtscommandeTransformat.+ restaurantet régionTesteurécarte cdesde testConsommat.écrit dansl’entrepôttubetubetubesourcemapfiltrepuitsChaque filtre ne connaît que ses formats d’entrée et de sortie, jamais ses voisinsLes tubes stockent et transmettent à sens unique
Quatre sortes de filtres reliés par des tubes ; chaque filtre peut être développé, testé et remplacé indépendamment.

Comment ça marche

Les contraintes produisent directement les avantages. Comme un filtre ne partage aucun état et ne connaît pas ses voisins, il peut être écrit et testé isolément, avec une liste d’enregistrements en entrée et une liste de sorties attendues ; il peut être réutilisé dans d’autres pipelines ; et les filtres peuvent être ajoutés, retirés ou réordonnés en modifiant uniquement la façon dont le pipeline est assemblé. Comme les enregistrements circulent de façon incrémentale, un filtre peut commencer à traiter le premier enregistrement avant que le filtre précédent ait fini le dernier : les étapes s’exécutent donc de façon concurrente, et l’ensemble du pipeline n’a besoin de mémoire que pour quelques enregistrements à la fois, et non pour tout le jeu de données.

Le tube masque la façon dont les données circulent. Dans un shell Unix, c’est un tampon du système d’exploitation entre deux processus ; sous Linux, il contient 64 Kio par défaut, et un écrivain qui le remplit se bloque simplement jusqu’à ce que le lecteur rattrape son retard. Dans un programme Python, ce peut être un générateur, de sorte que chaque filtre tire l’enregistrement suivant du précédent. Dans une plateforme de données, ce peut être une file de messages ou un topic dans un journal comme Apache Kafka, ce qui permet aux filtres de s’exécuter sur des machines différentes et de survivre aux redémarrages. Le code des filtres change à peine d’une solution à l’autre ; les propriétés opérationnelles, elles, changent beaucoup.

La forme stricte connaît des variantes. Un pipeline peut se ramifier (fan-out : un testeur aiguille les enregistrements vers deux consommateurs différents) ou converger (fan-in : deux producteurs alimentent un même transformateur). Il peut être linéaire, ce qui est le plus simple à raisonner, ou former un graphe orienté acyclique d’étapes, comme dans les outils de workflow tels qu’Apache Airflow. À strictement parler, un filtre doté d’un état interne, par exemple un filtre qui compte ou qui trie, rompt la propriété incrémentale, car il doit voir toute l’entrée avant de produire une sortie ; sort dans un pipeline Unix est un tel filtre. Ces filtres sont autorisés, mais ils deviennent les points où la mémoire, la latence et les redémarrages demandent réflexion.

Exemples : où l’on trouve des pipelines

ETL et pipelines de données. Les traitements Extract, transform, load (extraire, transformer, charger) déplacent les données des systèmes opérationnels vers un entrepôt de données : extraire les commandes de la veille, les nettoyer et les convertir, les joindre aux données des restaurants, les charger dans des tables de reporting. Chaque étape est un filtre, et les plateformes de données modernes (Apache Beam, Spark, modèles dbt, services cloud de flux de données) sont en grande partie des outils pour construire et exécuter de tels pipelines à grande échelle.

Compilateurs. Un compilateur classique est un pipeline de phases : l’analyse lexicale transforme des caractères en lexèmes (tokens), l’analyse syntaxique transforme les lexèmes en arbre syntaxique, l’analyse sémantique vérifie les types et les noms, l’optimisation réécrit une représentation intermédiaire, et la génération de code émet du code machine. Chaque phase consomme une représentation et produit la suivante, si bien que les phases peuvent être développées et remplacées séparément ; le projet LLVM, par exemple, permet à de nombreux frontends de langages de partager un même optimiseur et un même ensemble de backends. Les vrais compilateurs assouplissent le style strict, car les phases partagent une table des symboles.

Traitement multimédia. Le transcodage vidéo et le traitement audio enchaînent décodeurs, redimensionneurs, filtres et encodeurs. GStreamer construit explicitement les applications multimédias comme des pipelines d’éléments reliés par des pads, et les graphes de filtres de FFmpeg fonctionnent de la même façon.

Traitement des journaux et des événements. Les expéditeurs de journaux comme Logstash et Fluent Bit se configurent en étapes d’entrée, de filtrage et de sortie : lire les lignes de journal, les découper en champs, écarter le bruit des contrôles de santé, masquer les données personnelles et les envoyer vers un index de recherche. Les données d’observabilité de la plateforme de livraison de repas circulent de cette façon.

Pipelines de caractéristiques et d’inférence en apprentissage automatique. La préparation des données d’entraînement, ainsi que le prétraitement et le post-traitement autour d’un modèle, sont couramment construits comme des pipelines, auxquels revient le chapitre 12.

# Top ten URLs that returned HTTP 500 in today's access log
grep '" 500 ' access.log \
  | awk '{print $7}' \
  | sort \
  | uniq -c \
  | sort -rn \
  | head -10
Un pipeline Unix : six petits outils, chacun étant un filtre, trouvent les URL qui ont le plus souvent échoué avec une erreur HTTP 500 (dans le format courant des journaux d’accès, le code de statut suit la ligne de requête entre guillemets).
import json

def read_lines(path):                 # producer
    with open(path) as f:
        for line in f:
            yield line

def parse(lines):                     # transformer
    for line in lines:
        yield json.loads(line)

def only_failed(events):              # tester
    for e in events:
        if e["status"] == "payment_failed":
            yield e

def count_by_provider(events):        # consumer
    counts = {}
    for e in events:
        counts[e["provider"]] = counts.get(e["provider"], 0) + 1
    return counts

events = only_failed(parse(read_lines("orders.jsonl")))
print(count_by_provider(events))
La même idée avec des générateurs Python : chaque fonction est un filtre, et les enregistrements circulent un par un, si bien que le fichier n’est jamais chargé en mémoire d’un seul coup.

Batch et streaming

La même structure s’exécute selon deux modes. Le traitement par lots traite une entrée bornée, comme toutes les commandes de la veille, s’exécute jusqu’au bout et s’arrête. Il est simple à raisonner : si une exécution échoue, corrigez le problème et relancez-la, à condition que chaque exécution remplace sa sortie au lieu de s’y ajouter. Les versements nocturnes aux restaurants, les factures mensuelles et les chargements de l’entrepôt de données sont des pipelines batch typiques. Leur faiblesse est le délai : les résultats ont l’âge de la dernière exécution.

Le traitement de flux (stream processing) traite une entrée non bornée, en traitant chaque enregistrement peu après son arrivée : positions des livreurs, événements de paiement, clics. Les résultats sont frais, mais de nouvelles questions apparaissent. Un flux ne se termine jamais, donc les agrégats doivent être calculés sur des fenêtres (commandes par restaurant par tranche de cinq minutes) ; des enregistrements peuvent arriver en retard ou dans le désordre ; et le pipeline doit fonctionner en continu, se remettre des plantages sans perdre ni compter deux fois des enregistrements, et faire face aux pics de débit en entrée. Des frameworks comme Kafka Streams, Apache Flink et Apache Beam fournissent des fenêtres, des points de reprise (checkpoints) et des magasins d’état pour ces problèmes. Le modèle de Beam traite le batch comme un cas particulier du streaming, avec une entrée bornée, si bien qu’une même définition de pipeline peut s’exécuter dans les deux modes.

Beaucoup d’organisations utilisent les deux : un flux pour des tableaux de bord opérationnels frais mais approximatifs, et un traitement batch qui recalcule les chiffres exacts pendant la nuit. La section 7.8 examine ce choix du point de vue de l’architecture des données.

Exemple commenté

Exemple commenté : trouver le goulet d’étranglement

La plateforme de livraison de repas charge ses événements de commande dans son entrepôt de données au moyen de quatre filtres. Mesuré sur un seul worker, chaque filtre peut traiter à lui seul le nombre d’enregistrements par seconde indiqué ci-dessous.

CapacitésParse 50 000/s ; Enrich (recherche le restaurant et la région) 8000/s ; Validate 40 000/s ; Load 12 000/s.
Débit du pipelineUn pipeline ne peut pas aller plus vite que son filtre le plus lent : min(50 000 ; 8000 ; 40 000 ; 12 000) = 8000 enregistrements par seconde.
Durée pour les données d’une journéeUn jour de forte activité, la plateforme produit 1 000 000 d’événements de commande. 1 000 000 ÷ 8000 = 125 secondes.
Faire passer le goulet d’étranglement à l’échelleEnrich est sans état, donc deux instances peuvent s’exécuter en parallèle, chacune prenant la moitié des enregistrements : 2 × 8000 = 16 000/s à cette étape.
Nouveau débitmin(50 000 ; 16 000 ; 40 000 ; 12 000) = 12 000/s. Load est désormais le goulet d’étranglement. 1 000 000 ÷ 12 000 ≈ 83 secondes, soit un gain de 33 % et non des 50 % qu’une lecture naïve laisserait supposer.
Ce que font les tamponsSans back pressure, Parse pousserait des enregistrements dans le tube précédant Enrich à 50 000/s alors qu’Enrich n’en écoule que 8000/s : le tampon grossirait donc de 42 000 enregistrements par seconde jusqu’à épuisement de la mémoire.

Accélérer un autre filtre que le goulet d’étranglement ne change rien ; mesurez d’abord.

Seuls les filtres sans état peuvent être parallélisés aussi simplement. Un filtre qui conserve un état, comme des totaux cumulés par restaurant, doit partitionner son entrée par clé pour que chaque instance voie tous les enregistrements de ses clés.

C’est le même raisonnement que la loi d’Amdahl et le taux d’utilisation de la section 2.4 : l’étape séquentielle la plus lente fixe la limite.

Faire passer un pipeline à l’échelle déplace le goulet d’étranglement

Pipeline à quatre étapes dont le débit est limité par le filtre le plus lent : l’enrichissement, à 8000 enregistrements par seconde ; deux instances d’enrichissement y portent la capacité à 16 000, et le chargement, à 12 000, devient le nouveau goulet d’étranglementParse50 000/sEnrich8000/sValidate40 000/sLoad12 000/sgoulet d’étranglement : pipeline à 8000/sAprès exécution de deux Enrich en parallèleParse50 000/sEnrich 1 : 8000/sEnrich 2 : 8000/sValidate40 000/sLoad12 000/snouveau goulet : 12 000/s
Le débit est fixé par le filtre le plus lent ; le paralléliser aide jusqu’à ce qu’un autre filtre devienne le plus lent.

Back pressure et gestion des erreurs

Quand un producteur est plus rapide qu’un consommateur, quelque chose doit céder. Les options sont de mettre en tampon (ce qui ne fait que repousser le problème, sauf si le tampon est borné), d’écarter ou d’échantillonner des enregistrements (acceptable pour certaines métriques, jamais pour les paiements), ou d’appliquer un back pressure (contre-pression) : obliger l’étape rapide à ralentir jusqu’à la vitesse de l’étape lente. Les tubes Unix le font automatiquement, car un tube plein bloque l’écrivain. Les conceptions en mode pull, comme les générateurs Python, le font par construction, car rien n’est produit tant que l’étape suivante ne le demande pas. Les pipelines distribués doivent l’intégrer dès la conception : files bornées, consommateurs qui tirent les données à leur propre rythme depuis un journal, ou protocoles comme la spécification Reactive Streams de la JVM, dans laquelle un abonné signale combien d’enregistrements il est prêt à recevoir. La section 9.3 traite en profondeur des files d’attente et du back pressure.

Les erreurs exigent une politique pour chaque filtre, décidée à l’avance. Un enregistrement malformé (un message empoisonné, ou poison message) ne doit pas arrêter le pipeline : aiguillez-le vers une file de lettres mortes (dead-letter queue) ou un fichier d’erreurs avec le motif, comptez-le, et déclenchez une alerte si le taux augmente. Une défaillance transitoire, comme un service de recherche qui dépasse son délai d’expiration, donne lieu à de nouvelles tentatives avec backoff. Un plantage du pipeline exige un point de reprise : les traitements batch sont relancés depuis le début ou depuis un point de reprise (checkpoint), et les frameworks de streaming enregistrent périodiquement leur position et leur état. Comme les redémarrages impliquent que certains enregistrements soient traités deux fois, les consommateurs qui écrivent des résultats doivent être idempotents, par exemple en faisant un upsert sur une clé naturelle plutôt qu’une insertion à l’aveugle. Les erreurs sont le point faible du style : les filtres s’ignorent délibérément les uns les autres, si bien qu’une défaillance découverte à l’étape quatre ne peut pas facilement annuler ce que l’étape deux a déjà fait, et un pipeline qui a besoin de ce type de retour arrière coordonné relève probablement d’un autre style.

Caractéristiques des tubes et filtres (déploiement unique)

CaractéristiqueÉvaluationJustification
SimplicitéÉlevéChaque filtre fait une seule chose ; le flux est facile à dessiner, à lire et à expliquer.
Modularité et réutilisationÉlevéLes filtres ne partagent aucun état et ne connaissent que des formats de données, donc ils peuvent être réutilisés, réordonnés et remplacés.
TestabilitéÉlevéUn filtre se teste avec des exemples d’entrée et la sortie attendue, sans ses voisins.
Coût globalÉlevé (peu coûteux)Un seul programme ou traitement ; peu d’infrastructure, sauf en cas de distribution.
Débit (throughput)Moyen à élevéLes étapes s’exécutent de façon concurrente et les étapes sans état se parallélisent, mais le filtre le plus lent plafonne l’ensemble.
Latence et interactivitéBasConçu pour des flux de données, pas pour des échanges requête-réponse avec des utilisateurs ; chaque étape ajoute du délai.
DéployabilitéBas à moyenDans un seul programme ou traitement, toute modification redéploie l’ensemble du pipeline ; des filtres répartis sur des files peuvent être mis en production séparément.
Tolérance aux pannesBas à moyenDans un seul processus, un plantage arrête l’ensemble du pipeline ; points de reprise, nouvelles tentatives et files de lettres mortes doivent être ajoutés.
État partagé et transactionsBasLes filtres sont délibérément sans état et indépendants ; la cohérence ou le retour arrière entre étapes est malaisé.
ScalabilitéFaible à moyen dans un seul processusPlus élevé quand les filtres sont répartis sur des files, au prix d’un effort d’exploitation et de garanties de livraison à gérer.

Quand l’utiliser, quand l’éviter, et les anti-patterns

Utilisez les tubes et filtres quand le problème est naturellement un flux de données à sens unique à travers des transformations distinctes : ingestion, conversion, enrichissement, validation, agrégation, export. C’est le choix par défaut en ingénierie des données, pour le traitement de fichiers et de contenus multimédias, et pour tout traitement dont les étapes changent plus souvent que le flux global. Il apparaît aussi à l’intérieur d’autres styles : un traitement de reporting nocturne au sein d’un monolithe modulaire, ou un service de traitement de flux au sein d’un système piloté par les événements.

Évitez-le comme structure principale des applications interactives, où les utilisateurs attendent des réponses immédiates et conversationnelles et où le travail n’est pas un flux ; pour une logique qui exige que de nombreuses étapes partagent et modifient le même état ; et pour les processus qui nécessitent un retour arrière coordonné entre étapes, qui appellent une transaction ou une saga.

Anti-patterns courants : le filtre divin, une seule étape qui analyse, enrichit, valide et écrit, ce qui fait perdre les avantages du style ; l’état partagé caché, des filtres qui communiquent par une table de base de données ou une variable globale partagée au lieu du tube, ce qui réintroduit le couplage que le style supprime ; la dérive de format, un producteur qui change silencieusement son format de sortie et casse tous les filtres en aval, ce qu’empêchent des schémas explicites et des vérifications de schéma (voir la section 7.7) ; les tampons non bornés, qui transforment un consommateur lent en plantage par manque de mémoire ; et les puits non idempotents, qui comptent en double après chaque nouvelle tentative.

Faire respecter le style dans le code

Les contraintes du style peuvent être rendues explicites. Donnez à chaque filtre la même signature simple (un itérable d’enregistrements en entrée, un itérable en sortie), pour que la composition soit mécanique. Définissez le format des enregistrements à chaque tube sous forme de schéma (une dataclass, un JSON Schema, une définition Avro ou Protocol Buffers) et validez-le à la frontière. Gardez les filtres exempts d’état mutable partagé : un test d’architecture peut interdire aux modules de filtres d’importer la couche de base de données ou de s’importer entre eux, de sorte que le seul moyen pour les données de passer de l’un à l’autre soit le tube. Testez chaque filtre avec de petites fixtures d’entrée et de sortie, et testez le pipeline assemblé de bout en bout avec un échantillon représentatif, enregistrements malformés compris, pour prouver que la politique d’erreur fonctionne.

Dans les pipelines distribués, surveillez le débit par étape, la profondeur des files et le retard des consommateurs (consumer lag) : une file qui grossit devant une étape est le signe visible d’un goulet d’étranglement ou d’un filtre défaillant, et c’est la première métrique sur laquelle alerter.

Vérifiez votre compréhension

Quelles trois contraintes Garlan et Shaw ont-ils imposées aux filtres ?

Les filtres doivent être des entités indépendantes, ne doivent pas partager d’état avec d’autres filtres et ne doivent pas connaître l’identité des filtres situés en amont ou en aval.

Pourquoi la commande Unix sort rompt-elle la propriété incrémentale d’un pipeline ?

Elle doit lire toute son entrée avant de pouvoir produire la première ligne, donc elle garde l’ensemble des données et retarde tout ce qui la suit jusqu’à la fin de son entrée.

Un pipeline a des étapes qui peuvent traiter 20 000, 5000 et 15 000 enregistrements par seconde. Quel est son débit, et que se passe-t-il si l’étape du milieu est doublée ?

5000 par seconde, fixé par l’étape la plus lente. La doubler à 10 000 fait fonctionner le pipeline à 10 000 par seconde, toujours en dessous des deux autres étapes.

Pourquoi le consommateur final d’un pipeline redémarrable doit-il être idempotent ?

Parce qu’un redémarrage ou une nouvelle tentative peut livrer certains enregistrements deux fois ; une écriture idempotente, comme un upsert sur une clé naturelle, donne le même résultat quel que soit le nombre de fois où un enregistrement arrive.

Suivant · 5.5MicronoyauComment un petit système central et des plug-ins indépendants apportent l’extensibilité : registres, contrats, isolation et versionnage, exemples réels tirés des IDE et des navigateurs, et un contrat de plug-in commenté pour des règles fiscales propres à chaque pays.Continuer →

Sources et lectures complémentaires

Une question sur cette section ? ArchitectBot, l’assistant d’étude IA du cours, répond d’abord à partir du cours.

Demander : « Notre traitement ETL nocturne prend quatre heures et nous voulons qu’il en prenne moins d’une. Par où commencer à chercher ? »