X

AWS CDK 101 – Flux Dynamodb dĂ©clenchant le lot deleteObjects S3


🔰 DĂ©butants qui dĂ©couvrent AWS CDK, veuillez consulter mes articles prĂ©cĂ©dents un par un dans cette sĂ©rie.

Si au cas oĂč vous auriez manquĂ© mon article prĂ©cĂ©dent, retrouvez-le avec les liens ci-dessous.

🔁 Message prĂ©cĂ©dent original sur 🔗 Dev Post

🔁 RepubliĂ© le post prĂ©cĂ©dent Ă  🔗 dev Ă  @aravindvcyber

Dans cet article, ajoutons une intĂ©gration simple utilisant des flux dynamodb pour effectuer des suppressions par lots d’objets dans S3 que nous avons créés dans nos articles prĂ©cĂ©dents, et comprenons en quoi elle est plus performante qu’un seul appel d’objet de suppression S3.

Avantages dans cette approche 💩

  • Comme TTL dans dynamodb, nous avons des rĂšgles de cycle de vie qui vous permettent de supprimer les objets, mais cette approche illustrĂ©e ci-dessous est destinĂ©e Ă  une architecture plus dĂ©terministe et Ă©vĂ©nementielle oĂč vous avez besoin d’un post-traitement lors du nettoyage de ces enregistrements.
  • Bien qu’un seul objet de suppression soit Ă©galement bon, lorsque nous avons un grand nombre d’enregistrements les uns aprĂšs les autres, cela crĂ©era trop d’appels d’API et de surcharge en latence.
  • Les objets de suppression par lots nous aident Ă  rĂ©duire le nombre de demandes d’API externes.
  • RĂ©duction des pertes d’heures de calcul dues aux E/S externes dans une instance de mĂ©moire lourde pendant les temps d’inactivitĂ©.
  • Évitez d’ĂȘtre limitĂ© si nous avons atteint le dĂ©bit dĂ©fini pour le prĂ©fixe s3 Ă  de trĂšs rares occasions
  • Et il est Ă©galement possible que lambda expire si nous ne sommes pas prĂȘts Ă  l’augmenter
  • deleteObject ne fournit pas de rĂ©ponse complĂšte pour les buckets non versionnĂ©s, alors que deleteObjects fournit Ă©galement une rĂ©ponse soignĂ©e avec les erreurs.

MĂ©thodes de cycle de vie du spoiler pour faire expirer automatiquement les Ă©lĂ©ments 🍁

Oui, tout comme TTL dans dynamodb, nous pourrions utiliser les mĂ©thodes de cycle de vie pour expirer automatiquement ou faire passer des objets vers d’autres classes de stockage dans s3. Mais bon, cette pensĂ©e est la meilleure stratĂ©gie Ă  faire de cette maniĂšre. Chaque fois, nous finirons par avoir une fenĂȘtre beaucoup plus longue qui est nĂ©cessaire et nous l’oublierons et nous nous attendrons Ă  ce qu’elle se nettoie. Mais dans les deux derniers articles et celui-ci, nous essayons de notre mieux de configurer un moyen plus Ă©vĂ©nementiel pour effectuer ce nettoyage une fois que nous avons fini de traiter le message.

Cela peut Ă©galement ĂȘtre utilisĂ© comme derniĂšre option pour supprimer automatiquement certains artefacts orphelins aprĂšs une pĂ©riode dĂ©finie. Mais il a des objectifs diffĂ©rents et nous pourrons les couvrir peut-ĂȘtre dans un article ultĂ©rieur.

const stgMsgBucket = new s3.Bucket(this, 'stg-msg-bucket',{
      bucketName: envParams.bucket.BucketName,
      encryption: s3.BucketEncryption.S3_MANAGED,
      removalPolicy: RemovalPolicy.RETAIN,
      lifecycleRules: [{
        expiration: Duration.days(1),
        prefix: "uploads",
        id: "stg-msg-bucket-uploads-expiry-rule"
      }]
});

putItem renvoie Ă©galement un lors de la crĂ©ation de l’objet ☎

Activation des flux dynamodb dans notre table stgMessages đŸ’œ

Ce sera la table source censĂ©e gĂ©nĂ©rer les flux dynamodb. Mais la diffĂ©rence ici est que nous ne ciblerons que les flux gĂ©nĂ©rĂ©s lorsque des Ă©lĂ©ments sont supprimĂ©s. Nous allons donc essayer de supprimer le mĂȘme Ă©lĂ©ment clĂ© des compartiments s3 que nous avons initialement créés.

const stgMessages = new dynamodb.Table(this, "stgMessagesTable", {
      tableName: process.env.stgMessagesTable,
      sortKey: { name: "createdAt", type: dynamodb.AttributeType.NUMBER },
      partitionKey: { name: "messageId", type: dynamodb.AttributeType.STRING },
      encryption: dynamodb.TableEncryption.AWS_MANAGED,
      readCapacity: 5,
      writeCapacity: 5,
      stream: dynamodb.StreamViewType.KEYS_ONLY,
});

Fonction de gestionnaire dans notre pile 📌

Ici, nous avons dĂ©fini une fonction de gestionnaire que nous utiliserons dans notre rĂ©fĂ©rence pour accorder des privilĂšges et configurer la source de l’évĂ©nement, comme indiquĂ© ci-dessous.

const stgMessageStreamFunc = new lambda.Function(this, "stgMessageStreamFunc", {
      runtime: lambda.Runtime.NODEJS_14_X,
      code: lambda.Code.fromAsset("lambda"),
      handler: "stg-message-stream.deleted",
      logRetention: logs.RetentionDays.ONE_MONTH,
      tracing: Tracing.ACTIVE,
      layers: [nodejsUtils,nodejsXray],
      environment: {
        STAGING_MESSAGES_BUCKET_NAME: stgMsgBucket.bucketName,
      },  
});

stgMessageStreamFunc.applyRemovalPolicy(RemovalPolicy.DESTROY);

Accorder le privilùge deleteObject à la fonction de gestionnaire 📯


stgMsgBucket.grantDelete(stgMessageStreamFunc);

Configuration de nos flux dynamodb en fonction de gestionnaire 🔧


stgMessageStreamFunc.addEventSource(new DynamoEventSource(stgMessages, {
      startingPosition: lambda.StartingPosition.LATEST,
      batchSize:100,
      maxBatchingWindow: Duration.seconds(60)
}))

Pour cela crĂ©ons un nouveau fichier lambda/stg-message-stream.ts avec un nom de mĂ©thode d’exportation deleted

Notez que cela ne ciblera que les eventName comme REMOVE. Cela nous aidera avec les clés des éléments de table qui sont supprimés et nous pourrions les supprimer en utilisant notre gestionnaire comme suit de deux maniÚres.

Gestionnaire de suppression d’objet unique đŸŽș

Ici, nous utiliserons d’abord la suppression d’un seul objet et le comparerons avec la suppression d’un objet par lots, comme indiquĂ© ci-dessous.

exports.deleted = async function (event: any) {
  console.log("Received stream:", JSON.stringify(event, undefined, 2));
  const bucketName = process.env.STAGING_MESSAGES_BUCKET_NAME || "";
  const result = Promise.all(
    await event.Records.map(async (Record: DynamoDBStreams.Record) => {
      console.log(JSON.stringify(Record, undefined, 2));
      if (Record.eventName === "REMOVE") {
        const key = `uploads/${Record.dynamodb?.Keys?.messageId.S}.json`;
        console.log("keyId: ", key);
        const bucketParams: DeleteObjectRequest = {
          Bucket: bucketName,
          Key: key,
        };
        try {
          console.log("Deleting : ", bucketParams);
          const deleteObjectOutput: DeleteObjectOutput = await s3
            .deleteObject(bucketParams)
            .promise();
          console.log("Deleted : ", bucketParams);
          Object.entries(deleteObjectOutput).forEach((item) =>
            console.log({ item })
          );
          console.log(await s3.deleteObject(bucketParams).promise());
        } catch (err) {
          console.log("Error", err);
        }
      }
    })
  );
  Object.entries(result).forEach((item) => console.log({ item }));
};

Supprimez une seule transaction deleteObject 👜

Il ne faut pas plus d’une seconde pour supprimer l’objet s3 la plupart du temps, le fait que je veux souligner ici est la façon dont la surcharge est rĂ©partie entre les diffĂ©rentes couches.

Et cela devrait expliquer comment nous utilisons un lot de 100 flux maximum Ă  traiter Ă  partir du mĂȘme gestionnaire. Les frais gĂ©nĂ©raux sont simplement partagĂ©s et entiĂšrement divulguĂ©s, nos couches de surveillance sont Ă©galement intĂ©grĂ©es en tant que couches et elles ont des frais gĂ©nĂ©raux.

Enfin, dĂ©couvrons la vĂ©ritable rĂ©partition ci-dessous. Vous pouvez donc voir qu’il faut environ 50 ms Ă  300 ms pour terminer une seule demande d’API de suppression d’objet s3, comme indiquĂ© ci-dessous.

Effets secondaires notables 🌂

Si nous ne les regroupons pas de maniĂšre appropriĂ©e, nous nous retrouverons dans les efforts nĂ©gatifs ci-dessous et quelques objets peuvent ĂȘtre orphelins si nous ne rĂ©essayons pas.

  • perdre les heures de calcul en raison d’E/S externes
  • peut-ĂȘtre Ă©tranglĂ© si nous avons atteint le dĂ©bit dĂ©fini pour le prĂ©fixe s3 en de rares occasions
  • Et il est Ă©galement possible que lambda expire si nous ne sommes pas prĂȘts Ă  l’augmenter

Analyse du temps pris single delete 🍝

Vous pouvez constater que dans ces traces newrelic, la demande d’API est effectuĂ©e plusieurs fois l’une aprĂšs l’autre.

Vous pouvez dire que je peux les faire en parallĂšle, nous finirons Ă©galement par limiter la requĂȘte API au prĂ©fixe s3 lorsque nous crĂ©ons trop de requĂȘtes parallĂšles lorsque nous en exĂ©cutons plusieurs Ă  la fois dans le pire des cas.

Gestionnaire de suppression d’objets par lots 🍝

Puisque nous sommes conscients des points ci-dessus, effectuons une demande par lots maintenant et voyons le redressement.


exports.deleted = async function (event: any) {
  console.log("Received stream:", JSON.stringify(event, undefined, 2));
  const bucketName = process.env.STAGING_MESSAGES_BUCKET_NAME || "";
  const keyMap: any[] = [];
  const result = Promise.all(
    await event.Records.map((Record: DynamoDBStreams.Record) => {
      console.log(JSON.stringify(Record, undefined, 2));
      if (Record.eventName === "REMOVE") {
        const key = `uploads/${Record.dynamodb?.Keys?.messageId.S}.json`;
        console.log("keyId: ", key);
        keyMap.push(key);
      }
    })
  );
  if (keyMap.length > 0) {
    const bulkParams: DeleteObjectsRequest = {
      Bucket: bucketName,
      Delete: {
        Objects: [],
        Quiet: false,
      },
    };
    keyMap.map((key) => {
      const object: ObjectIdentifier = {
        Key: key,
      };
      bulkParams.Delete?.Objects?.push(object);
    });
    try {
      console.log("Deleting Batch : ", bulkParams);
      const deleteObjectsOutput: DeleteObjectsOutput = await s3
        .deleteObjects(bulkParams)
        .promise();
      console.log("Deleted Batch : ", bulkParams);
      Object.entries(deleteObjectsOutput).forEach((item) =>
        console.log({ item })
      );
    } catch (err) {
      console.log("Error", err);
    }
  }
  Object.entries(result).forEach((item) => console.log({ item }));
};

Exemple de lot gĂ©nĂ©rĂ© Ă  partir de mon test k6 🚀

Ici, dans cet exemple, 26 Ă©lĂ©ments sont pris dans un seul lot. Mais notez que la taille de notre lot est de 100, mais il est Ă©galement possible que nos articles fassent Ă©galement partie de 2 sĂ©ries successives, en fonction de la fenĂȘtre d’interrogation que nous avons dĂ©finie sur les annĂ©es 60.

Analyse du temps pris pour une suppression unique ✈

L’appel de suppression par lots S3 peut prendre plus de temps que la suppression unique, mais le point Ă  noter ici sera que nous pouvons Ă©viter la surcharge et la latence impliquĂ©es de maniĂšre exponentielle lorsque nous Ă©voluons cela, disons jusqu’à 1000 objets.


Suppression des autres couches de surveillance entraßnant des frais généraux

Ceci conclut cet article.

Nous ajouterons plus de connexions à notre pile et la rendrons plus utilisable dans les prochains articles en créant de nouvelles constructions, alors pensez à suivre et à vous abonner à ma newsletter.

⏭ Nous avons notre prochain article en serverless, consultez

🎉 Merci pour votre soutien ! 🙏

Ce serait formidable si vous aimez ☕ Achetez-moi un cafĂ©, pour aider Ă  stimuler mes efforts.

🔁 Message original sur 🔗 Dev Post

🔁 RepubliĂ© Ă  🔗 dev Ă  @aravindvcyber

☔ AWS CDK 101 – ⛅ Flux Dynamodb dĂ©clenchant batch deleteObjects S3
@hashnode

En savoir plus sur ma pagehttps://t.co/CuYxnKI3Kg#TheHashnodeWriteathon#manuscrit #awscdk #aws #sans serveur #thwcloud-informatique https://t.co/3aoXjnp6lk

— Aravind V (@Aravind_V7) 21 mai 2022