AWS Lambda

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 mye 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

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert.