【问题标题】:Querying DynamoDB with Lambda does nothing使用 Lambda 查询 DynamoDB 没有任何作用
【发布时间】:2015-08-24 17:43:49
【问题描述】:

我有以下 Lambda 函数代码:

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();
};

我有一个这样定义的 [Users] 表:

{
    "cognitoID": { "S": "token" },
    "email": { "S": "user1@domain.com" },
    "password": { "S": "somepassword" },
    "tos_aggreement": { "BOOL": true },
    "userID": { "S": "user1" }
}

当我(从 AWS 控制台或 CLI)调用函数时,我可以在日志中看到消息,但从未调用 getItem() 的回调。

我尝试在没有回调的情况下执行 getItem(params),然后定义了完成、成功和失败的回调,但是当我执行 send() 时,甚至也不调用完整的回调。

我知道调用是异步的,我想也许 lambda 函数在查询完成之前就完成了,因此不会调用回调,但是,我在函数末尾添加了一个简单的愚蠢循环,并且调用在 3 秒后超时,根本没有调用回调。

我尝试了不同的函数 batchGetItem、getItem、listTables 和扫描。结果是一样的,没有错误,但是回调函数永远不会被调用。

我敢打赌,如果我在不使用 Lambda 的情况下查询 dynamoDB,它会得到结果,所以我真的想知道为什么这里什么都没有发生。

我为该函数创建了一个角色,并创建了一个策略,允许访问 dynamoDB 中我需要但无济于事的功能。

政策如下所示:

{
    "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": "*"
        }
    ]
}

我在模拟器中运行了该策略,它按我想的那样工作。 有什么建议吗?

【问题讨论】:

  • 您是否收到消息说进程在完成请求之前已退出?
  • 不,因为我在那里有一个 context.done() 或 context.succeed() 调用。问题是 JSNode 脚本是异步的,当您调用 dynamoDB 时,由于 lambda 函数终止,它的回调可能永远不会被调用。事实上,dynamoDB 操作甚至没有时间开始。

标签: javascript node.js callback amazon-dynamodb aws-lambda


【解决方案1】:

所以,事实证明代码是正确的。 问题是 dynamodb API 使用了所有这些回调,并且基本上该函数在数据被检索之前就结束了。

最快的解决方法是删除context.succeed() 调用,然后将检索数据。 当然使用 async 模块会有所帮助,如果您不想使用它,只需在回调中添加一个计数器或布尔值,然后等到值发生更改,表明回调已被调用(哪种很烂如果你想的话)

【讨论】:

  • 我认为最好从回调函数中调用context.succeed(),而不是添加一个计数器和循环,就像这样:console.log(data); context.succeed(数据);
【解决方案2】:

我遇到了一些类似的问题,但没有找到很多有用的资源。这就是我最终做的事情。也许更聪明的人可以告诉我们这是否是最好的。

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);
});

总之,通过主函数将回调传回给较小的函数,可以让应用程序继续运行,直到完成 Dynamo。将 context.succeed 保留在辅助函数中或在此处继续其他函数。

【讨论】:

  • 这就是我一直这样做的方式。如果您想到它,问题不在于 dynamoDB 或 Lambda,而在于 Lambda 函数是 nodeJS 代码这一事实,这意味着一切都是异步的,因此您需要到处都有回调。有时它不是很方便,但这只是将你的头脑围绕在异步模式下编码的问题:)
  • 哇,我真的很高兴你发布了这个。我一直试图让它工作几个小时。我没有看到任何与您在文档中的内容相似的内容,但它有效!。
【解决方案3】:

为了避免回调地狱,请使用 Promises。 youtube 上有一些很不错的教程,作者是 funfunfunction。

【讨论】:

    【解决方案4】:

    我的问题是我的 lambda 在 VPC 中运行以连接到 ElastiCache。这会导致对公共 Internet 资源(例如 DynamoDB 和 API Gateway)的任何查询无限期挂起。我必须在我的 VPC 中设置一个 NAT 网关才能访问 DynamoDB。

    【讨论】:

    • 这个答案对我很有帮助,但我创建了另一个 lambda 来连接到 ElasticCache,这样我就不必将 NAT 网关添加到我的 VPC,然后我的 VPC 中只有 ElastiCache lambda 并且我调用它来自我的主要 lambda(不在 VPC 中)
    【解决方案5】:

    现在由于 node.js 引入了 async/await,这可以让它等到查询调用返回,然后主函数终止:

    let result = await ddb.getItem(params).promise();
    return result;
    

    【讨论】:

      【解决方案6】:

      我的问题是我试图调用的函数接受了回调

      因此,Node.js 只是继续执行 Lambda 函数,一旦到达终点,它告诉 Lambda 关闭,因为它已经完成

      这是一个如何使用 Promises 解决问题的示例:

      'use strict';
      
      exports.handler = async (event, context) => {
      
        // WRAP THE FUNCTION WHICH USES A CALLBACK IN A PROMISE AND RESOLVE IT, WHEN
        // THE CALLBACK FINISHES
      
        return await new Promise( (resolve, reject) => {
      
          // FUNCTION THAT USES A CALLBACK
          functionThatUsesACallback(params, (error, data) => {
            if(error) reject(error)
            if(data) resolve(data)
          });
      
        });
      
      };
      

      【讨论】:

      • 该死!我正在修复 cloudwatch.logs 思考为什么日志没有输出。实际问题隐藏在这里。非常感谢朋友。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-04
      • 1970-01-01
      相关资源
      最近更新 更多