【问题标题】:Why is my AWS Lambda node.js mysql query not returning?为什么我的 AWS Lambda node.js mysql 查询没有返回?
【发布时间】:2019-06-23 01:36:12
【问题描述】:

我正在尝试将一些外部数据写入一些本地表中。我们将遍历一个数组,将每个数组元素中的大部分数据写入主表,将其余数据写入相关表,每次都替换所有数据。

我已将代码精简到最基本的部分,以显示我遇到的问题。 DELETE 运行良好,但 INSERT 只运行一次,甚至不返回。

我在https://imgur.com/a/zA6Hz8g 有输出的屏幕截图。 在其中,您可以看到 DELETE 的代码运行良好(发送了 ComQueryPacket,返回了 OkPacket),但是当它到达 INSERT 时,发送了 ComQueryPacket 但没有返回任何内容。然后代码就失败了。

这会导致第一行写入成功,但不会写入后续行。

我尝试将连接更改为使用池,但这也无济于事。

有什么想法吗?

var mysql = require('mysql');
var promise = require('promise');

const con = mysql.createConnection({
    <connectionInfo>,
    debug: true
});

function connectToDB() {
    return new promise((resolve, reject) => {
        console.log("IN connectToDB");
        con.connect(function(err) {
            if (err) {
                console.log("ERROR: Could not connect -- " + err);
                reject;
            }
            console.log("Connected!");
            resolve();
        });
    });
}

function deleteExistingMainRow() {
    return new promise((resolve, reject) => {
        var query = "DELETE FROM my_table";
        con.query(query, [],
            function(err, result) {
                if (err) {
                    console.log("ERROR in deleteExistingMainRow: " + err);
                    reject;
                }
                else {
                    console.log("DEBUG: Successful delete of main row");
                    resolve();
                }
            });
    });
}

function writeMainRow(data_row) {
    return new promise((resolve, reject) => {
        console.log("IN writeMainRow");
        var query = 'INSERT INTO my_table SET id = ?';

        con.query(query, [data_row.id],
            function(err, result) {
                console.log("YES we tried to query");
                if (err) {
                    console.log("ERROR in writeMainRow: " + err);
                    reject(err);
                }
                else {
                    console.log("DEBUG: Successful write of main row");
                    resolve();
                }
            });
    });
}

exports.handler = function(event, context) {
    connectToDB().then(function(script) {
        deleteExistingMainRow().then(function(script) {
            var data = [{ "id": 1 }, { "id": 2 }, { "id": 3 }];
            data.forEach(data_row => {
                writeMainRow(data_row).then(function(script) {
                        console.log("DEBUG: Main row written in forEach");
                    },
                    function(err) {
                        if (err) { console.log("ERR"); } process.exit(0);
                    }());
            });
            console.log("DEBUG: Hey we're exiting now");
            con.commit;
            con.end(function(err) {
                console.log("Error on con end: " + err);
            });
            context.done(null, "DONE");
            process.exit(0);
        });
});

};

【问题讨论】:

    标签: mysql node.js amazon-web-services lambda


    【解决方案1】:

    您的代码未按预期运行的原因是 NodeJS 的异步特性。

    您的 for_each 循环产生了几个线程,这些线程将在您的数据库中插入数据。
    这些线程一启动,剩下的代码就会执行,从console.log("DEBUG: Hey we're exiting now");开始

    所以提交发生在所有 INSERT 调用完成之前,更重要的是,您在代码中调用 Process.exit()。这会终止运行时,甚至在 INSERT 完成之前。 按照https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html 调用 callback()

    可以以不同的方式处理多个异步写入。首先,正如 grynets 在我之前评论的那样,我强烈建议使用 async/await 重写您的代码,以使调用更易于阅读。 然后,您必须了解对writeMainRow 的每次调用都将返回其自己的Promise,并且您的代码必须等待所有承诺完成,然后才能到达commit()callback()

    Promise.all(...) 会为你做这件事。请参阅https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all 的文档

    【讨论】:

      【解决方案2】:

      尝试使用 插入表名(id)值(?); 我知道你的查询和上面的查询都是一样的。试试看吧。

      只需确保您的 for 循环正常工作,即可将值发送到 writeMainRow(function)。如果传递一个空值并确保您没有在 for 循环中传递相同的值,它不会显示错误。而且我认为您必须通过 writeMainRow(data_row.id) 而不是 writeMainRow(data_row)

      希望这会有所帮助。 如果您要更新多行,还有一个建议,在 mysql 节点库中有一些选项,例如事务。使用这些函数会更有效,如果遇到错误,您可以回滚结果。另一种选择是编写程序,在这种情况下你的mysql服务器将承担计算。

      【讨论】:

      • 谢谢沃尔特。我最初确实有 VALUES,但它以相同的方式工作,我在我的一次迭代中将其更改为这个以使其正常工作。而且我知道这些值已正确发送,因为我取出了将值记录到控制台的 cmets。另外,我需要data_row,而不仅仅是id,因为还有其他字段是从data_row写入的;为了这篇文章,我只把它精简到了最基本的部分。
      • 你 con.commit() 不是承诺回报的一部分。在更新查询之前,您已经调用了 con.comit() 函数。而且我认为如果您在安全更新模式下运行 mysql,则不需要 con.commit() 。另一种方法是将整个数组传递给 writeMainRow() 函数,for 循环所有数据并返回一个 Promise。一旦收到承诺,则提交连接,如果有错误回滚。希望这会有所帮助
      【解决方案3】:

      就在几个月前 AWS made Node.js v 8.10 运行时在 lambda 中可用。
      这意味着,您可以使用 async/awaitPromises
      因此,我们可以将代码重新排列为:

      exports.handler = async (event, context) => {
           const dbConnection = await connectToDB();
      
           await deleteExistingMainRow();
      
           const data = [{ "id": 1 }, { "id": 2 }, { "id": 3 }];
      
           // use here for...of loop to keep working with async/await behaviour
           for(const data_row of data){
             await writeMainRow(data_row);
           }
      }
      

      此外,您可以重写代码以使用本机 Promisesasync/await 函数。
      当然,关于 try/catch 块的覆盖逻辑,为了简单起见,我已经跳过它们。

      【讨论】:

      • 虽然这似乎是一个合理的答案,但它并不能解释为什么原始代码会失败。具体来说,您不应该在 forEach 循环中调用异步代码(例如调用 writeMainRow),因为循环在异步调用完成之前终止,并且代码在所有写入发生之前退出。
      • Jarmod,我最初在循环中有 writeMainRow 的代码,但由于 JS 的异步特性,我发现循环在所有行都写完之前就终止了。这就是为什么我转向承诺。您是否有示例说明如何执行我需要执行的操作(为每个传入数据点写入多条记录)并等到完成后再退出?
      • @jarmod 我完全同意你的看法,这就是我使用for...of 而不是forEach 的原因。我在回答中提到了这一点。
      • @Saul to >(为每个传入数据点写入多条记录)并等到完成后再退出?你可以在我的回答中使用for...of
      • @jarmod,请看这个解释lavrton.com/…
      猜你喜欢
      • 1970-01-01
      • 2017-06-24
      • 1970-01-01
      • 2011-11-07
      • 2019-11-13
      • 1970-01-01
      • 2014-09-27
      • 1970-01-01
      • 2020-05-17
      相关资源
      最近更新 更多