Cette liste d’optimisation n’est évidement pas exhaustive, il s’agit d’une sélection personnelle sur les nombreux points détaillés lors de la session Best practices for advanced serverless developers de Julian Wood lors de l’AWS re:Invent 2022 à Las Vegas.
Petit rappel
Nous en avons parlé dans un précédent article (“La fin du cold start sur les lambda”) des optimisations apportées par AWS sur les temps de démarrage de certaines fonctions lambdas. Mais comme pour tout service cloud, la responsabilité est partagée, et ce, y compris pour les problématiques d’optimisation.
Ci-dessous, se trouve une représentation des étapes de démarrage d’une fonction lambda avec le porteur des optimisations :
Les SnapStart mis en place par AWS remplacent les trois premières étapes par une seule qui consiste à restaurer le snapshot créé lors de la dernière initialisation.
Optimisation du code pre-handler
Côté utilisateur, l’optimisation de l’initialisation de la fonction au travers de toutes les opérations réalisées avant le handler
est une première étape.
Voici quelques conseils pour cette étape :
- Ne chargez que ce dont vous avez besoin : N’importer que les modules nécessaires pour chaque librairie
Exemple :
// Instead of const AWS = require('aws-sdk'), use:
const DynamoDB = require('aws-sdk/clients/dynamodb') // 125 ms faster
// Instead of const AWSXRay = require('aws-xray-sdk'), use:
const AWSXRay = require('aws-xray-sdk-core') // 5 ms faster
// Instead of const AWS = AWSXRay.captureAWS(require('aws-sdk')), use:
const dynamodb = new DynamoDB.DocumentClient()
AWSXRay.captureAWSClient(dynamodb.service) // 140 ms faster
Source : https://docs.aws.amazon.com/lambda/latest/operatorguide/static-initialization.html
- Éviter d’y gérer les connexions : Ne pas gérer les initialisations de client ou les connexions à des bases de données dans les opérations de pre-handler.
- Utiliser le lazy loading pour les modules partagés : N’initialiser les modules que lorsqu’ils sont nécessaires puis les réutiliser pour les prochains appels
Exemple :
import boto3
s3_client = None
ddb_client = None
def get_objects_handler(event, context):
if not s3_client:
s3_client = boto3.client("s3")
# business logic
deg get_items_handler(event, context):
if not ddb_client:
ddb_client = boto3.client("dynamodb")
# business logic
Source : https://serverlessland.com/reinvent2022/svs401
Dimensionnement de votre lambda
Comme vous le savez, vous pouvez paramétrer la quantité de mémoire à allouer à vos fonctions entre 128 Mo et 10 Go. AWS de son côté va proportionnellement allouer plus de capacité CPU et de bande passante réseau en fonction de ce paramétrage.
Source : https://serverlessland.com/reinvent2022/svs401
Avec cela en tête, il est possible d’ajuster l’allocation de ressource en fonction des besoins de votre fonction. Si votre process est consommateur en CPU, vous pouvez allouer plus de mémoire pour obtenir plus de vCPU.
Sur cette page de la documentation, AWS prend pour exemple 1000 exécution d’une lambda qui calcule tous les nombres premiers et obtient les résultats suivants :
Mémoire allouée | Temps moyen d’exécution | Coût |
128 MB | 11.722 s | $0.024628 |
256 MB | 6.678 s | $0.028035 |
512 MB | 3.194 s | $0.026830 |
1024 MB | 1.465 s | $0.024638 |
Si on compare la première et la dernière ligne, on peut observer que pour une hausse du coût de 0.00001 $ pour 1000 exécutions, le temps de traitement est passé de presque 12 secondes à environ 1,5 seconde.
Vous trouverez sur ce GitHub un outil permettant d’automatiser le profilage de vos lambda : https://github.com/alexcasalboni/aws-lambda-power-tuning
Favoriser l’asynchrone
À part dans les cas de figure où il est indispensable d’avoir recours à des appels synchrones, il ne faut pas hésiter à tirer parti des services AWS permettant de découpler les composants d’une architecture.
AWS SQS, AWS SNS, AWS Event Bridge AWS Kinessis sont autant de services qui permettent d’interfaces les sources d’évènements et leur cible en ajoutant une couche d’asynchronisme. Ces points d’interconnexion simplifient le raccordement de futurs modules complémentaires qui formeront des architectures Event Driven.
“ Events are the language of serverless applications ! “
Conclusion
Enfin évidement, une part importante des optimisations en termes de performance pour vos lambdas va se faire sur le code en lui-même. Mais pour cela, il n’y a pas de formule magique. Cela va dépendre du langage, de vos contraintes ainsi que de vos objectifs.
Merci pour votre lecture. Si cet article vous a plu, merci de le partager sur vos réseaux 😉
Timothée Taron - 30 Nov 2022
VOUS AVEZ UN PROJET ?
Audit, migration, infogérance ?
Skale-5 vous écoute : contact@skale-5.com
Nous suivre :