Lambda 函数完成,但对 RDS 的查询不完整

Posted

技术标签:

【中文标题】Lambda 函数完成,但对 RDS 的查询不完整【英文标题】:Lambda function completes, but query to RDS is incomplete 【发布时间】:2020-04-11 14:52:30 【问题描述】:

我试图使用 handler.async 对数据库 (RDS) 进行后查询。 但是,我遇到了以下问题。

有一半时间,lambda 函数完成,但查询未成功发送到 RDS。另一半时间,它将完全发送到 lambda。尝试添加一个 setTimeout 函数以将 lambda 执行时间增加 3 秒,并且查询将一直发送。

日志也会显示错误:

INFO 错误:致命错误后无法将查询排入队列。

以下是我的代码:

var mysql = require('mysql');
var connection = mysql.createConnection(
  host     : '***',
  user     : '***',
  password : '***',
  database : '***'
);


exports.handler = async (event) => 
const sql = `INSERT INTO forms VALUES(777,2,3,4,5,6,7,8,9,10,11);`;
const query = (x) => 
  return new Promise ((resolve,reject)=>
    resolve(connection.query(x, function (error, results, fields) 
      console.log(error)
      console.log(results)
      console.log(fields)
      
)))
await query(sql)

具有超时功能,

var mysql = require('mysql');
var connection = mysql.createConnection(
  host     : '***',
  user     : '***',
  password : '***',
  database : '***'
);


exports.handler = async (event) => 
const sql = `INSERT INTO forms VALUES(777,2,3,4,5,6,7,8,9,10,11);`;
const query = (x) => 
  return new Promise ((resolve,reject)=>
    resolve(connection.query(x, function (error, results, fields) 
      console.log(error)
      console.log(results)
      console.log(fields)
      
)))
await query(sql)

await wait(3000)





const wait = (x) => 
  return new Promise ((resolve,reject)=>
    setTimeout(()=>resolve(console.log("delay")), x);
  )

第一个值是主键。发送一个常量 777 进行检查,如果错误显示主键重复,则表示查询发送成功。如果没有错误,则表示尽管 lambda 完成,但查询未成功发送。

execution result succeeded but shows:
START RequestId: e541fe4b-6927-4fbb-90b4-750f77e5f460 Version: $LATEST
2019-12-19T01:54:45.212Z    e541fe4b-6927-4fbb-90b4-750f77e5f460    INFO    Error: **Cannot enqueue Query after fatal error**.
    at Protocol._validateEnqueue (/var/task/node_modules/mysql/lib/protocol/Protocol.js:212:16)
    at Protocol._enqueue (/var/task/node_modules/mysql/lib/protocol/Protocol.js:138:13)
    at Connection.query (/var/task/node_modules/mysql/lib/Connection.js:201:25)
    at /var/task/index.js:14:24
    at new Promise (<anonymous>)
    at query (/var/task/index.js:13:10)
    at Runtime.exports.handler (/var/task/index.js:20:7)
    at Runtime.handleOnce (/var/runtime/Runtime.js:66:25) 
  code: 'PROTOCOL_ENQUEUE_AFTER_FATAL_ERROR',
  fatal: false
2019-12-19T01:54:45.213Z   e541fe4b-6927-4fbb-90b4-750f77e5f460    INFO    undefined2019-12-19T01:54:45.213Z   e541fe4b-6927-4fbb-90b4-750f77e5f460    INFO    undefined2019-12-19T01:54:45.262Z   e541fe4b-6927-4fbb-90b4-750f77e5f460    INFO    delayEND RequestId: e541fe4b-6927-4fbb-90b4-750f77e5f460
REPORT RequestId: e541fe4b-6927-4fbb-90b4-750f77e5f460  Duration: 51.09 ms  Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 80 MB  

请您建议并告诉我执行它的最佳方法是什么??

【问题讨论】:

请帮助任何人? 【参考方案1】:

在任何环境中管理 RDBMS 连接都不是一件容易的事。 Lambda 在这里增加了一层复杂性。您需要了解热重启和冷重启之间的区别,这对于在处理程序函数之外创建的资源意味着什么,何时适合使用连接池,以及何时以及如何释放连接。

与数据库的持久连接并不特别适合微服务、FaaS 环境(如 Lambda)。这就是 Aurora Serverless 支持 HTTP Data API 的原因之一(希望其他数据库引擎在某个时候也能支持)。

阅读How To: Manage RDS Connections from AWS Lambda Serverless Functions。

还要注意新的Amazon RDS Proxy with AWS Lambda。

在您的特定情况下,最明显的问题是您重复创建数据库连接但从未释放它们(除非这是我不知道的 mysql 包的 query 函数的内置功能)。

【讨论】:

@excitelybored 如果提供的回复之一有助于回答您的问题,请选择它作为答案。如果没有,请考虑写一个答案并选择它。【参考方案2】:

您可以将 lambda 超时时间延长至 15 分钟。但是如果你通过 api 网关调用 lambda,超时是 29 秒。

这是为我工作的代码。

const mysql = require('mysql');
const con = mysql.createConnection(
  host: process.env.RDS_HOSTNAME,
  user: process.env.RDS_USERNAME,
  password: process.env.RDS_PASSWORD,
  port: process.env.RDS_PORT,
  connectionLimit: 10,
  multipleStatements: true,// Prevent nested sql statements
  debug: true
  // ,database:'testdb1'
);


exports.handler = async (event) => 
  try 
    const data = await new Promise((resolve, reject) => 
      con.connect(function (err) 
        if (err) 
          reject(err);
        
        const sql = `INSERT INTO forms VALUES(777,2,3,4,5,6,7,8,9,10,11);`;
        con.query(sql, function (err, result) 
          if (err) 
            console.log("Error->" + err);
            reject(err);
          
          resolve(result);
        );
      )
    );

    return 
      statusCode: 200,
      body: JSON.stringify(data)
    

   catch (err) 
    return 
      statusCode: 400,
      body: err.message
    
  
;

参考:aws lambda with rds mysql DDL command not working

【讨论】:

我在您的代码中看到的主要问题是,您没有正确解析承诺,它应该是connection.query(x, function (error, results, fields) resolve() ),而是将查询函数作为参数传递给解析函数。我不明白为什么。 我认为那是因为您没有释放连接。查询完成后尝试做connection.release()。或者您可以在配置中配置一个池 嗨阿伦克。我设法让它工作。非常感谢。只是一个简单的新手问题,是用于调试的try..catch。我可以删除它们还是更好地放置它们。 尝试捕捉很重要。首先,它将帮助您从 api 返回有关异常的友好消息。如果没有 try catch ,应用程序将从 api 发送确切的丑陋错误。 其次,对于promise的拒绝必须要try catch,在以后的node版本中,如果promise拒绝没有在app内部处理,nodejs进程将会终止

以上是关于Lambda 函数完成,但对 RDS 的查询不完整的主要内容,如果未能解决你的问题,请参考以下文章

在CloudFront访问上触发RDS lambda

AWS Lambda + NAT 网关的替代方案

使用 rds-data 增加来自 execute_sql 的 aws lambda 结果计数的 1000 限制或使用不同的包?

RDS 触发器的 Lambda 函数访问导致连接丢失

Python AWS Lambda 为每个部署旋转到 RDS 的新连接

如何为 AWS RDS 凭证创建配置文件并将其导入我的 AWS Lambda API?