Le Blog Amazon Web Services

DynamoDB Streams : cas d’usages et design patterns

Cet article décrit les cas d’usages communs, ainsi que les options de design et les solutions, lors de la migration d’une base de données relationnelle vers Amazon DynamoDB.

Nous examinons comment gérer les scénarios suivants :

  • Comment configurer une relation entre plusieurs tables dans lesquelles, en fonction de la valeur d’un élément d’une table, vous mettez à jour l’élément dans une deuxième table
  • Comment déclencher un évènement basé sur la modification d’un élément particulier
  • Comment auditer ou archiver les données
  • Comment répliquer les données sur plusieurs tables (similaire à ce que permettent des vues matérialisées/flux de données/réplication dans des bases de données relationnelles)

Vous pouvez utiliser DynamoDB Streams pour adresser tous ces cas d’usage. DynamoDB Streams est un service que vous pouvez combiner avec d’autres services AWS pour résoudre de nombreux problèmes similaires. Lorsque vous activez DynamoDB Streams, il capture une séquence chronologique de modifications au niveau des objets d’une table DynamoDB et stocke les informations de manière durable jusqu’à 24 heures. Les applications peuvent accéder à un flux d’enregistrements contenant les changements d’éléments, à partir d’un flux DynamoDB, en temps quasi réel.

AWS gère des points d’accès distincts DynamoDB Streams et DynamoDB. Pour accéder aux tables et aux indexes de la base de données, votre application doit accéder à un point d’accès DynamoDB. Pour lire et traiter des enregistrements DynamoDB Streams, votre application doit accéder à un point d’accès DynamoDB Streams dans la même région.

DynamoDB Streams

Figure 1 : Accès à DynamoDB et DynamoDB Streams

DynamoDB Streams supporte les vues des flux d’enregistrements suivants :

  • KEYS_ONLY—Uniquement les attributs clés de l’élément modifié
  • NEW_IMAGE—L’image de l’élément modifié, après modification
  • OLD_IMAGE—L’image de l’élément modifié, avant modification
  • NEW_AND_OLD_IMAGES—A la fois l’ancienne et la nouvelle image de l’élément modifié

Vous pouvez traiter les flux DynamoDB Streams de plusieurs façons. La façon la plus commune consiste à utiliser AWS Lambda, ou bien une application autonome utilisant Kinesis Client Library (KCL) avec l’adaptateur DynamoDB Streams Kinesis. KCL est une bibliothèque client fournissant une interface de traitement des flux de modifications DynamoDB Streams. l’adaptateur DynamoDB Streams Kinesis traite ce flux afin d’avoir une vue sur les enregistrements uniques retournés par DynamoDB Streams. Lambda exécute votre code basé sur un évènement de DynamoDB Streams (insertion/mise à jour/suppression d’un élément). Dans cette approche, le service AWS Lambda scrute le flux DynamoDB Streams et, dès qu’il détecte une modification, appelle la fonction Lambda en lui passant un ou plusieurs éléments en paramètre.
Design patterns DynamoDB Streams

Le schéma suivant décrit une architecture de référence pour différents cas d’usages reposant sur DynamoDB Streams et d’autres services AWS.

design patterns

Figure 2 : design patterns DynamoDB Streams

Considérons un cas d’usage simple de stockage et de consultation de transactions de factures depuis une table DynamoDB nommée  InvoiceTransactions. Une seule facture peut contenir plusieurs milliers de transactions par client. La clé de partition est InvoiceNumber, et la clé de tri est TransactionIdentifier afin de supporter l’unicité et de fournir des capacités d’interrogation en utilisant la clé InvoiceNumber. Le schéma suivant montre le design de la table.

Nom de la table : InvoiceTransactions

Partition Key Sort Key Attribute1 Attribute2 Attribute3 Attribute4
InvoiceNumber TransactionIdentifier Amount Trans_country Invoice_dt InvoiceDoc
1212121 Client1_trans1xxxx $100 USA 6062016 {JSON Doc1}
1212121 Client1_trans2xxxx $500 USA 6062016 {JSON Doc2}
1212122 Client2_trans1xxx $200 UK 6062016 {JSON Doc3}
1212121 Client2_trans1xxxx $500 China 6062016 {JSON Doc4}

Maintenant, supposons qu’on insère un nouvel élément

1212123 Client3_trans1xxx $1,000 USA

Après cette insertion, le flux DynamoDB Streams possède l’entrée suivante.

Type de vue Valeurs
New image TransactionIdentifier= Client3_trans1xxx,InvoiceNumber=1212123,Amount-$1000,Trans_country=USA
Keys only InvoiceNumber=1212123, TransactionIdentifier= Client3_trans1xxx

Maintenant, supposons que, vu la nature de ce cas d’usage, l’application demande des capacités d’audit, de recherche, d’archivage, de notification et d’agrégation à chaque modification dans la table InvoiceTransactions. Examinons comment vous pourriez utiliser le flux de données pour couvrir différents cas d’usages.

Implémentation de capacités transactionnelles avec plusieurs tables

DynamoDB supporte les transactions, pour implémenter une logique métier qui nécessite plusieurs opérations “tout ou rien” sur les éléments d’une ou de plusieurs tables. Vous pouvez utiliser les APIs de transaction côté serveur de DynamoDB pour adresser ces cas d’usages, avec un modèle de consistance forte.

Cependant, si votre besoin est d’utiliser DynamoDB comme une vue matérialisée sur des données stockées sur une ou plusieurs tables, pour une recherche rapide ou une validation, vous pouvez utiliser les données du flux pour ce faire. Ces vues matérialisées possèdent généralement des patterns d’accès et des caractéristiques différentes. Il faut garder à l’esprit que vous pouvez appliquer des modifications d’une façon éventuellement consistante. Voici quelques exemples :

  • Agrégation : Considérons un scenario dans lequel vous avez besoin de maintenir le montant total en dollars pour chaque facture, pour des besoins de reporting. Ce total devrait être mis à jour dynamiquement quand des nouveaux éléments sont insérés dans la table InvoiceTransactions ou d’autres tables en relation, et maintenu à jour. Pour calculer le total, créer une nouvelle table avec la structure suivante :

Nom de la table : InvoiceTotal

Clé de partition Attribut numérique Attribut chaîne de caractères
InvoiceNumber Total Update_date
1212121 $1,100 6062016
1212122 $200 6072016

A chaque modification dans la table InvoiceTransactions, vous mettez à jour le total. Essayons de faire ceci en utilisant une expression de mise à jour comme ceci:

TableName": "InvoiceTotal",
"Key": {
"InvoiceNumber": 1212121 }
UpdateExpression = "ADD Total :Amount SET update_date = :date"

La valeur :Amount peut être lue depuis le flux de modifications DynamoDB à chaque fois qu’un élément est ajouté à la table InvoiceTransactions, et :date peut être la date en cours. L’opérateur ADD est la commande. Pour un attribut numérique, il additionne la valeur spécifiée à l’attribut. SET est un autre opérateur de commande. Il indique que les attributs suivants vont être mis à jour.

  • Agrégation conditionnelle : Considérons un scenario dans le quel vous avez besoin d’enregistrer le nombre de transactions par pays pour les factures qui sont affectées à un responsable pour vérification. Dans ce cas, vous créez une nouvelle table InvoiceAction avec la structure suivante:

Nom de la table : InvoiceAction

InvoiceNumber (Clé de partition) Trans_country (clé de tri) Verify_action Total_trans
1212121 USA admin@com 2
1212121 UK admin@co.uk 1
1212121 China

A chaque modification dans la table InvoiceTransactions, vous mettez à jour le total en utilisant une expression de  mise à jour avec une écriture conditionnelle comme ceci:

TableName": "InvoiceAction",
"Key": {
"InvoiceNumber": 1212121, Trans_country=”USA” }
UpdateExpression = "SET Total_Trans = Total_trans+1"
ConditionExpression = “attribute_exists(verify_action)”

Cette opération échoue avec une erreur ConditionalCheckFailedException pour les pays dans lesquels il n’y a pas de responsable affecté — par exemple, la Chine dans ce scenario.

  • Réplication : Dans les bases de données relationnelles, il existe des outils qui sont généralement utilisés pour répliquer les données depuis une table maitre vers des tables filles, pour des besoins de requêtage (déchargement des requêtes/réplication des données). De la même manière, en utilisant DynamoDB Streams, vous pouvez répliquer la table InvoiceTransactions vers des régions différentes pour des besoins de faible latence ou de reporting. Vous pouvez utiliser DynamoDB global tables pour répliquer les données vers d’autres régions AWS. Les tables globales utilisent DynamoDB Streams afin de propager les modifications entre les replicas dans les différentes régions AWS.

Archivage/audit

Cas d’usage : Supposons que l’on ait un besoin métier de stocker toutes les transactions des factures pour une durée allant jusqu’à 7 ans, pour des besoins de conformité ou d’audit. Les utilisateurs ont également besoin de lancer des requêtes ad-hoc sur ces données.

Solution : DynamoDB est idéal pour stocker les données (chaudes) temps réel qui sont accédées fréquemment. Après un certain temps, dépendant du cas d’usage, les données ne sont plus “chaudes”, et sont typiquement archivées dans un système de stockage comme Amazon S3. Vous pouvez concevoir une solution utilisant Amazon Kinesis Data Streams, Amazon Kinesis Data Firehose, et Amazon S3. Kinesis Data Firehose est un service entièrement géré que vous pouvez utiliser pour charger le flux de données dans Amazon S3, Amazon Redshift, ou Amazon OpenSearch Service, en utilisant des appels d’API. Il peut également lotir, compresser, transformer et chiffrer les données avant de les charger, ce qui permet d’économiser l’espace de stockage utilisé et de renforcer la sécurité

Ce qui suit est une description de haut niveau de la solution.

  • Créer un nouveau flux de données dans Kinesis Data Streams (par exemple, ddbarchive).
  • Activer le streaming vers Kinesis sur une table DynamoDB en utilisant la console ou l’API. A chaque création, modification, ou suppression d’un élément dans la table InvoiceTransactions, DynamoDB enverra un enregistrement vers Kinesis.
  • Créer un flux de livraison Kinesis Data Firehose pour traiter les enregistrements depuis ddbarchive, et les stocker dans une destination comme Amazon S3, pour persister les données du flux DynamoDB. Par défaut, Kinesis Data Firehose ajoute un préfixe de temps UTC au format YYYY/MM/DD/HH avant d’écrire les fichiers sur S3. Vous pouvez modifier cette structure de répertoire en ajoutant votre répertoire de premier niveau (par exemple, Invoice/YYYY/MM/DD/HH pour stocker les transactions de facture).
  • Kinesis Data Firehose lotit les données et les stocke dans Amazon S3 en se basant soit sur une taille tampon (1-128 Mo), soit sur une intervalle tampon (60-900 seconds). Le premier critère rempli déclenche l’envoi des données vers Amazon S3.
  • Mettre en place des règles de cycle de vie S3 pour déplacer les données vers la classe de stockage S3-IA (accès moins fréquent) ou Amazon Glacier afin de réduire les coûts.
  • Vous pouvez également utiliser DynamoDB Time to Live (TTL), qui simplifie l’archivage en supprimant automatiquement des éléments basés sur un attribut horodaté. Par exemple, vous pouvez désigner Invoice_dt comme étant un attribut TTL en stockant sa valeur au format epoch. Pour un exemple d’implémentation, vous pouvez lire l’article (en anglais) Automatically Archive Items to S3 Using DynamoDB TTL with AWS Lambda and Amazon Kinesis Firehose.
  • Une option alternative consiste à utiliser les fonctionnalités natives de DynamoDB pour exporter vers S3 (article en anglais), sans devoir écrire du code. Une fois vos données stockées dans votre bucket S3, vous pouvez utiliser Amazon Athena pour lancer des requêtes ad hoc sur les données, à des fins d’audit et de conformité.

Reporting

Cas d’usage : Comment lancer des requêtes rapides en temps réel sur DynamoDB ?

Solution : Concevoir le schéma des tables DynamoDB basé sur les besoins du reporting et ses patterns d’accès. Par exemple, si vous avez besoin d’un reporting en temps réel sur les transactions de facture, vous pouvez accéder aux données de factures ou transactions directement en utilisant les appels d’API Query ou GetItem. Concevez votre schéma avec une clé de partition (ou la combinaison d’une clé de partition et d’une clé de tri) adapté aux besoins des requêtes. De plus, vous pouvez créer des indexes locaux secondaires et des indexes globaux secondaires pour supporter l’accès à la table via d’autres attributs.

Exemple : les requêtes suivantes peuvent être utilisées sur des tableaux de bord temps-réel. Dans cet exemple, la table invoiceTotal possède les attributs total, update_date, etc., et est partitionnée par invoice_number. La table invoiceTransactions possède les attributs InvoiceNumber et TransactionIdentifier. Elle est partitionnée sur ces deux attributs, en utilisant InvoiceNumber comme clé de partition et Transaction_Identifier comme clé de tri (clé primaire composite). Vous avez les besoins suivants pour votre reporting en temps réel :

  • Lister toutes les transactions pour un InvoiceNumber donné. Solution : Appeler l’API Query en utilisant InvoiceNumber comme clé de partition, sans utiliser la clé de tri.
  • Lister toutes les transactions pour un InvoiceNumber tel que l’attribut TransactionIdentifier commence par client1_.xxx. Solution : Appeler l’API Query en utilisant InvoiceNumber comme clé de partition, et une expression de condition sur la clé TransactionIdentifier.
  • Lister le total par InvoiceNumber. Solution : Appeler l’API GetItem avec la clé InvoiceNumber.

Cas d’usage : Comment exécuter des requêtes analytiques sur des données stockées dans DynamoDB ?

Solution : Pour les cas d’usage peu fréquents, vous pouvez utiliser Amazon Athena Federated Query ou Amazon EMR avec HiveQL pour exécuter des requêtes directement sur DynamoDB. DynamoDB est optimisé pour les transactions en ligne, où la majorité des opérations sur les données sont indexées (et matérialisées – pour éviter des variations des performances). Pour les cas d’analyses fréquentes, il est généralement préférable d’exporter les données DynamoDB (périodiquement ou par réplication en continu basée sur un flux) vers un entrepôt de données comme Amazon Redshift, qui est optimisé, par son format de stockage, pour exécuter de façon plus efficace des agrégations de données.

Voici un résumé de cette solution :

  • Amazon Redshift est un entrepôt de données géré, qui supporte nativement l’exécution de requêtes analytiques complexes.
  • Activer le streaming sur la table DynamoDB, ce qui active la capture d’évènements en temps quasi réel dans un flux de données Kinesis. Créer le flux de livraison Firehose pour consommer le flux de données, en configurant Amazon Redshift comme destination.
  • Kinesis Data Firehose utilise un bucket S3 intermédiaire, et la commande COPY pour charger les données dans Amazon Redshift.
  • Utiliser Amazon QuickSight ou un outil standard de BI (Business Intelligence) pour lancer des requêtes sur Amazon Redshift.
  • Pour plus d’informations sur l’implémentation d’un pipeline de données utilisant Kinesis Firehose, Amazon Redshift, et Amazon QuickSight, se référer à l’article (en anglais) Amazon Kinesis – Setting up a Streaming Data Pipeline.

Option alternative : utiliser la commande COPY d’Amazon Redshift pour lire la table DynamoDB et la charger dans Amazon Redshift. Référez vous à la documentation DynamoDB pour plus de détails.

Exemple : Les requêtes similaires à celles-ci sont mieux exécutées sur Amazon Redshift.

  • Obtenir le montant journalier depuis la table invoiceTransactions sur les trois dernières années.
  • Sélectionner max(invoice_amount), sum(total_amount), et count(invoice_trans) depuis la table Invoice Transactions group_by YYYYHHMMDD.

Notifications/messages

Cas d’usage : Supposons un scénario avec la table InvoiceTransactions, où à chaque fois qu’une valeur zéro est insérée ou mise à jour dans l’attribut amount, l’équipe concernée doit immédiatement être notifiée pour action.

Solution : Construire une solution utilisant DynamoDB Streams, Lambda et Amazon SNS pour traiter ce scénario.

Voici un résumé de la solution :

  • Définir une rubrique SNS et des abonnements (e-mail ou SMS).
  • Utiliser Lambda pour lire le flux DynamoDB et vérifier si le montant de la facture est de zéro. Puis, publier un message vers la rubrique SNS. Par exemple : “Merci de prendre une action sur la facture 1212121 ayant un montant à zéro dans la table InvoiceTransactions à YYMMHH24MISS”
  • Les abonnés reçoivent des notifications en quasi temps réel et peuvent entreprendre les actions appropriées.

Pour plus d’informations sur l’implémentation de cette solution, référez vous à l’article (en anglais) Building NoSQL Database Triggers with Amazon DynamoDB and AWS Lambda.

Cas d’usage : Supposons un scénario où à chaque saisie d’une nouvelle facture, les données doivent être envoyées à un système en aval d’exécution des paiements.

Solution : Vous pouvez construire une solution utilisant DynamoDB Streams, Lambda, Amazon SNS, et Amazon SQS pour traiter ce type de scénarios. Supposons que le système aval de paiement attende un message SQS pour déclencher un processus de paiement.

Voici un résumé de la solution :

  • Définir une rubrique Amazon SNS avec Amazon SQS comme abonné. pour plus de détails, référez vous à la  Documentation d’Amazon SNS.
  • Utiliser une fonction Lambda pour lire le flux DynamoDB et vérifier s’il y a une nouvelle transaction de facturation, puis envoyer un message SNS.
  • Le message SNS est délivré à la file SQS.
  • Dès que le message arrive, le système en aval peut interroger la file SQS et déclencher le traitement.

Recherche
Cas d’usage : Comment réaliser des recherche par texte libre, sur DynamoDB ? Par exemple, supposons que la table InvoiceTransactions contient un attribut InvoiceDoc de type document, contenant un document JSON tel que décrit dans le tableau si-dessous. Comment filtrer sur une transaction client en particulier, ou bien interroger des données (quantité de “printers”/“desktops”, noms de fournisseurs contenant %1%, etc.) dans l’attribut de type document sur DynamoDB?

Clé de partition Clé de tri Attribute4
InvoiceNumber TransactionIdentifier InvoiceDoc
1212121 Client1_trans1xxxx Partition Key Sort Key Attribute4 InvoiceNumber TransactionIdentifier InvoiceDoc 1212121 Client1_trans1xxxx { “Vendor Name”: “Vendor1 Inc”, “NumberofItems”: “5”, “ItemsDesc”: [ { “laptops”: { “Quantity”: 2 }, “desktops”: { “Quantity”: 1 }, “printer”: { “Quantity”: 2 } } ] }

Solution : DynamoDB n’est pas adapté pour de la recherche par texte libre sur des larges volumes de données. nous recommandons d’utiliser Amazon OpenSearch Service pour adresser ce type de besoins.

Voici un résumé de la solution :

  • Dans ce design, la table DynamoDB InvoiceTransactions est utilisé comme entrepôt primaire. un cluster Amazon OpenSearch est utilisé pour fournir tous les types de recherche en indexant la table InvoiceTransactions table.
  • En utilisant DynamoDB streams, toute création, mise à jour ou suppression d’un élément dans la table est capturé puis traité par une fonction Lambda. Cette dernière effectue les appels nécessaires à Amazon OpenSearch pour indexer les données en temps quasi réel.
  • Pour plus d’informations sur cette architecture, référez vous à l’article (en anglais) Indexing Amazon DynamoDB Content with Amazon Elasticsearch Service Using AWS Lambda ou bien Loading Streaming Data into Amazon ES from Amazon DynamoDB
  • Comme alternative, vous pouvez utiliser la fonctionnalité de streaming pour envoyer les modification vers Amazon OpenSearch via un flux de livraison Firehose. Avant de charger les données dans Amazon OpenSearch, vous pourriez avoir besoin d’effectuer des transformations sur ces données. Vous pouvez utiliser des fonctions Lambda pour ceci; pour plus d’informations, se référer à Data Transformation.

OpenSearch supporte également tous les types de recherche par texte libre, incluant le tri et l’agrégation des résultats. La flexibilité est un autre avantage de cette approche. OpenSearch Query peut être facilement modifié pour ajouter de nouveaux filtres, et Amazon OpenSearch fournit nativement cette possibilité. Par exemple, si vous ajoutez un nouvel attribut dans la table DynamoDB, il sera automatiquement disponible pour des requêtes sur Amazon OpenSearch.

Meilleures pratiques en utilisant DynamoDB Streams

Gardez à l’esprit ces meilleures pratiques lorsque vous concevez des solutions utilisant DynamoDB Streams :

  • DynamoDB Streams vous permet de construire des solutions utilisant une synchronisation des données en quasi temps réel. Elle n’assure pas une consistance ou une gestion des transactions sur plusieurs tables. Ceci doit être géré au niveau de l’application. Il faut également être conscient de la latence (inférieure à une seconde) dans le traitement du flux de données. Ceci vous permet de définir le SLA concernant la disponibilité des données dans vos applications en aval et auprès des utilisateurs finaux.
  • Toutes les modifications d’un élément seront dans le flux, y compris les suppressions. Votre application doit être capable de traiter les suppressions, mises à jour et créations.
  • Concevez votre composant de traitement du flux pour traiter tous les types d’exceptions. Assurez vous de stocker vos données de flux dans une “dead letter queue” (DLQ) comme SQS ou S3, pour un traitement différé des échecs.
  • Les échecs peuvent se produire dans l’application qui lit l’évènement depuis le flux. Vous pouvez concevoir l’application de façon à minimiser le risque et l’étendue de l’erreur/incident. Pour cela, essayez de ne pas mettre à jour plusieurs tables au sein du même code. De plus, vous pouvez concevoir vos tables de façon à mettre à jour plusieurs attributs d’un même élément (plutôt que cinq attributs différents, par exemple). Vous pouvez également concevoir votre traitement de façon à ce qu’il soit idempotent, ce qui vous permet de rejouer le traitement en cas d’erreur. Vous devriez également capturer les différentes exceptions dans votre code et décider si vous souhaitez les rejouer les enregistrements ou bien les ignorer et les enregistrer dans la DLQ pour une analyse ultérieure
  • Vous devez être conscients des contraintes suivantes lorsque vous concevez des applications consommatrices:
    • La rétentions des données du flux DynamoDB Streams est de 24 heures
    • Au plus deux processus peuvent lire depuis le flux simultanément.
  • On vous recommande de considérer Lambda pour traiter le flux quand cela est possible, car Lambda est serverless et donc plus simple à gérer. Évaluez d’abord si Lambda peut être utilisé, et si ce n’est pas le cas, vous pouvez utiliser la librairie client Kinesis (Kinesis Client Library – KCL). La table de comparaison suivante vous permet de décider:
Paramètre AWS Lambda Kinesis Client Library
Déploiement Lambda scrute le flux DynamoDB stream et appelle votre fonction/code dès qu’un nouvel enregistrement est détecté. Vous écrivez votre propre application utilisant KCL avec DynamoDB Streams Kinesis Adapter, et l’hébergez sur une instance EC2.
Facilité de gestion Lambda scale automatiquement en fonction de la charge Vous devez utiliser les shards, la surveillance, la mise à l’échelle, et des points de contrôle en phase avec les meilleures pratiques KCL. (Pour plus d’informations, voir cet article (en anglais) sur la conception et cet article (en anglais) sur la surveillance.)
Comment cela fonctionne Pour chaque partition DynamoDB, il y a un shard correspondant, et une fonction Lambda scrute les évènements dans le flux (shard). En fonction de la taille de batch que vous spécifiez, la fonction extrait les enregistrements, les traite, puis passe au batch suivant.

– Enumère les shards dans le flux

– Coordonne l’association des shards avec d’autres processus (s’il y en a)

– Instancie un processeur d’enregistrement pour chaque shard à traiter

– Récupère les enregistrements depuis le flux

– Envoie les enregistrements au processeur d’enregistrements correspondant

– Effectues les vérifications de point de contrôle des enregistrements traités

Temps d’exécution Le temps d’exécution maximum par requête est de 900 secondes Il n’y a pas de restriction
Disponibilité Lambda est un service géré par AWS et hautement disponible. Il n’y a pas de fenêtre de maintenance ou de temps d’arrêts programmés. L’application doit être hébergée au sein d’un groupe d’Autoscaling EC2 pour la haute disponibilité
Autres considérations Notez l’impact des limites des payloads Lambda en utilisant Lambda pour consommer des flux. Toute donnée de type _IMAGE peut la dépasser, en particulier pour les attributs larges. Vous devez donc spécifier une taille de lot en prenant cela en compte.

Récapitulatif

DynamoDB Streams est un service que vous pouvez combiner avec d’autres services AWS afin de créer des solutions pratiques pour migrer depuis des bases de données relationnelles vers DynamoDB. Cet article décrit quelques cas d’usage communs et les solutions à retenir, avec les bonnes pratiques que vous devriez respecter en utilisant DynamoDB Streams.

Article initialement publié par Gowri Balasubramanian Solutions Architect, et adapté en français par Moncef Atouf, Solutions Architect dans les équipes AWS France.