2015-06-09 11 views
14

ho il seguente codice per una funzione lambda:Interrogazione DynamoDB con Lambda non fa nulla

console.log('Loading function'); 
var aws = require('aws-sdk'); 
var ddb = new aws.DynamoDB(); 

function getUser(userid) { 
    var q = ddb.getItem({ 
     TableName: "Users", 
     Key: { 
      userID: { S: userid } } 
     }, function(err, data) { 
      if (err) { 
       console.log(err); 
       return err; 
      } 
      else { 
       console.log(data); 
      } 
    }); 
    console.log(q); 
} 


exports.handler = function(event, context) { 
    console.log('Received event'); 
    getUser('user1'); 
    console.log("called DynamoDB"); 
    context.succeed(); 
}; 

Ho una tabella [gli utenti] che si definisce come tale:

{ 
    "cognitoID": { "S": "token" }, 
    "email": { "S": "[email protected]" }, 
    "password": { "S": "somepassword" }, 
    "tos_aggreement": { "BOOL": true }, 
    "userID": { "S": "user1" } 
} 

Quando chiamo il funzione (da AWS Console o CLI) Posso vedere i messaggi nei log ma il callback per getItem() non viene mai chiamato.

Ho provato a eseguire getItem (params) senza richiamata, quindi definito i callback per il completamento, il successo e l'errore ma quando eseguo send(), neanche il callback completo viene chiamato.

So che le chiamate sono asincrone e ho pensato che forse, la funzione lambda stava finendo prima che la query fosse terminata e quindi il callback non sarebbe stato chiamato, ma, ho aggiunto un semplice ciclo stupido alla fine della funzione e la chiamata è scaduta dopo 3 secondi, senza che vengano richiamati i callback.

Ho provato con diverse funzioni batchGetItem, getItem, listTables e scan. Il risultato è lo stesso, nessun errore ma la funzione di callback non viene mai chiamata.

Scommetto che se interrogassi dynamoDB senza usare Lambda, otterrò i risultati quindi mi sto davvero chiedendo perché non sta succedendo nulla qui.

Ho creato un ruolo per la funzione e ho creato una politica che consentisse l'accesso alle funzionalità in dynamoDB di cui ho bisogno, ma senza risultato.

La politica si presenta così:

{ 
    "Version": "2012-10-17", 
    "Statement": [ 
     { 
      "Effect": "Allow", 
      "Action": [ 
       "lambda:InvokeFunction" 
      ], 
      "Resource": "arn:aws:lambda:*:*:*" 
     }, 
     { 
      "Effect": "Allow", 
      "Action": [ 
       "dynamodb:GetItem", 
       "dynamodb:BatchGetItem", 
       "dynamodb:Scan", 
       "dynamodb:PutItem", 
       "dynamodb:Query", 
       "dynamodb:GetRecords", 
       "dynamodb:ListTables" 
      ], 
      "Resource": "arn:aws:dynamodb:*:*:*" 
     }, 
     { 
      "Action": [ 
       "logs:*" 
      ], 
      "Effect": "Allow", 
      "Resource": "*" 
     } 
    ] 
} 

ho eseguito la politica nel simulatore e ha funzionato come ho pensato che sarebbe. Suggerimenti?

+0

hai ricevuto un messaggio che indica che il processo è stato chiuso prima di completare la richiesta? –

+0

No perché ho avuto una chiamata context.done() o context.succeed(). Il problema è che lo script JSNode è asincrono e quando si effettua una chiamata a dynamoDB, i suoi callback potrebbero non essere mai chiamati poiché la funzione lambda termina. In effetti, l'azione dynamoDB non ha nemmeno il tempo di iniziare. –

risposta

13

Quindi, il codice è corretto. Il problema è che l'API dynamodb utilizza tutti quei callback e sostanzialmente la funzione termina PRIMA che i dati siano stati recuperati.

La soluzione più rapida è rimuovere la chiamata context.succeed() ei dati verranno recuperati. Naturalmente l'aiuto del modulo asincrono sarebbe d'aiuto e se non vuoi usarlo, aggiungi un contatore o un booleano al tuo callback e attendi finché il valore non è cambiato, indicando che è stato chiamato il callback (che tipo di fa schifo se ci pensate)

+6

Invece di aggiungere un contatore e il ciclo per niente, penso che sia meglio chiamare 'context.succeed()' dalla funzione di callback, in questo modo: \t console.log (dati); \t context.succeed (dati); –

4

Ho avuto alcuni problemi simili e non ho trovato molte risorse utili. Ecco cosa ho finito per fare. Probabilmente qualcuno più intelligente può dirci se questo è il migliore.

function getHighScores(callback) { 
    var params = { 
     TableName : 'sessions', 
     ScanFilter: {"score":{"AttributeValueList":[{"N":"0"}], "ComparisonOperator":"GT"}}, 
    }; 
    var dynamo = new AWS.DynamoDB(); 
    dynamo.scan(params, function(err, data) { 
     if (err) { 
      console.log (err) 
      callback(err); 
     } else { 
      callback(data.Items); 
      console.log(data.Items); 
     } 
    }); 
} 



getHighScores(function (data) { 
    console.log(data); 
    context.succeed(data); 
}); 

In sintesi, avendo il pass-back del callback attraverso la funzione principale della funzione più piccolo, consente all'applicazione di continuare fino a completare la dinamo. Mantenere il contesto con successo nella funzione secondaria o continuare altre funzioni lì.

+0

Questo è il modo in cui l'ho fatto. Se ci pensate, il problema non è dynamoDB o Lambda, ma piuttosto il fatto che la funzione Lambda è il codice nodeJS, il che significa che tutto è asincrono e quindi è necessario avere callback dappertutto. A volte non è molto comodo, ma è solo questione di avvolgere la tua mente codificando tutto in modalità asincrona :) –

+0

Wow, sono davvero contento che tu abbia postato questo. Ho cercato di farlo funzionare per ore. Non ho visto nulla di simile a quello che hai qui nei documenti, ma funziona !. – dudeman

0

Per evitare l'inferno di callback, utilizzare Promises. Ci sono alcuni tutorial piuttosto buoni su youtube da ragazzo chiamato funfunfunction.

2

Il mio problema è che il mio lambda era in esecuzione in un VPC per connettersi a ElastiCache.Ciò causa l'interruzione indefinita di qualsiasi query a risorse Internet pubbliche come DynamoDB e API Gateway. Ho dovuto configurare un gateway NAT all'interno del mio VPC per poter accedere a DynamoDB.

+0

Questa risposta mi ha aiutato molto ma ho creato un altro lambda per connettermi a ElasticCache quindi non avrei dovuto aggiungere il NAT Gateway al mio VPC, quindi solo il lambda ElastiCache è nel mio VPC e lo invoco dal mio lambda principale (cioè non nel VPC) – fpg1503