Etter å ha knotet med AWS Lambda i over en måned har jeg lært mye nyttig som kanskje kan hjelpe andre som skal bruke denne superkjekke tjenesten.
(Det er lite kodeeksempler i AWS’ API-dokumentasjon så hver gang man skal ta i bruk ny funksjonalitet blir det gjerne noe prøving og feiling kombinert med googling.)
Første problemet man som regel opplever er at ens egen Lambda-funksjon avslutter før kall til andre AWS-tjenester returnerer et resultat (eller error). Dette er simpelthen fordi kallet er asynkront og kanskje trenger flere hundre millisekunder før det fullfører.
For å vente på asynkrone kall har man heldigvis flere muligheter.
Promise
En mye brukt, men fortsatt ganske ny løsning, er promise:
const AWS = require('aws-sdk'); exports.handler = async () => { var dynamodb = new AWS.DynamoDB(...); await dynamodb.putItem(...).promise().then(function() { // Kode for suksess her }).catch(function(error) { // Kode for feil her }); // Synkron kode fortsetter her };
Her er det await og promise som styrer showet. Og på promise lenkes then også catch. Etterpå kan man fortsette med annen synkron kode. Dette er en veldig ryddig måte å gjøre det på. Dessverre er det ikke alle AWS API-er som har promise.
Om det er flere asynkrone kall som skal gjennomføres bør man fjerne await over og vente på kallene samtidig med Promise.all:
... var dynamodb = new AWS.DynamoDB(...); var promise1 = dynamodb.putItem(...).promise(); var promise2 = <annet asynkront API-kall her>.promise(); // o.s.v. Promise.all([promise1, promise2, ...])
Også henger man på then og catch her i stedet for å ha det på hver promise over. Men dette er selvsagt bare mulig så lenge kallene kan kjøres uavhengig av hverandre.
Hendelser
Man har også anledning til å bruke hendelser:
const AWS = require('aws-sdk'); exports.handler = () => { var dynamodb = new AWS.DynamoDB(...); var kall = dynamodb.putItem(...); kall.on('success', function(respons) { // Kode for suksess her }); kall.on('error', function(error) { // Kode for feil her }); kall.send(); // Synkron kode // fortsetter her };
Tilbakekall
Alternativt har man bruk av tilbakekall (altså «callback» på engelsk), men hvor man dessverre havner i "callback helvete" hvis man skal gjøre mange kall:
const AWS = require('aws-sdk'); exports.handler = () => { var dynamodb = new AWS.DynamoDB(...); dynamodb.putItem(..., function(error, data) { if (error) { // Kode for feil her } else { // Kode for suksess her } }); };
Problemet med alle eksemplene over er at det blir unødig venting. Og siden man betaler for hver påbynte bolk med 100 millisekunder er det viktig å begrense ventingen så mye som mulig.
Eksemplet nedenfor viser den raskeste måten jeg har funnet til nå:
const AWS = require('aws-sdk'); exports.handler = (hendelse, kontekst, tilbakekall) => { kontekst.callbackWaitsForEmptyEventLoop = false; var dynamodb = new AWS.DynamoDB(...); dynamodb.putItem(..., function(error) { if (error) { // Kode for feil her } else { // Kode for suksess her } tilbakekall(null, ...); }); };
Gjentatte tester viste at tidsforbruket minst ble halvert.
Å eksperimentere med AWS Lambda er noe man aldri blir ferdig med ..
TBC