pg-promise - 为什么我们应该使用batch()而不是Promise.all?

标签 pg-promise

来自 pg-promise 常见问题解答 Why use method batch instead of promise.all? :

It is quintessential to settle all the promises-queries created within your task or transaction, before the connection is released

我不明白为什么这会成为一个问题。

例如,当我们有一组像这样的查询时:

  [
    t.any("SELECT pg_sleep(2) as a"),
    t.any('this will fail'),
    t.any("SELECT pg_sleep(3) as b")
  ]

注意:pg_sleep仅用于测试。
在生产中,这将是 Insert/Update/Delete 语句。我们只想在全部成功时提交事务:即当其中任何一个失败时返回错误。

当我们使用batch()时:

  • 第一个 promise 将在 2 秒后解决
  • 第二个 promise 将被拒绝
  • 第三个查询仍将发送到数据库并在 3 秒后返回
  • 最后(总共 5 秒后),batch 完成,我们可以向调用者返回错误。

当我们使用Promise.all()时:

  • 第一个 promise 将在 2 秒后解决
  • 第二个 Promise 将拒绝 - 这将回滚事务并释放数据库连接
  • 现在我们已经可以向调用者返回错误
  • 第三个请求将立即失败,并显示查询已释放或丢失的连接。。无论如何,这是预料之中的,所以我们可以忽略它。

所以我想说 Promise.all 更好,因为:

  • 它在第一个错误发生后立即返回
  • 甚至不会向数据库发送第三个无用的查询

我错过了什么?
这是否可能会导致其他问题:例如断开的连接返回到池等。

最佳答案

方法batch适合可能创建动态数量的查询的场景。

它确保所有查询都得到解决(解决或拒绝),因此您最终不会针对关闭的连接执行查询,并收到针对已释放或丢失的连接进行查询错误。开始让这些错误发生在上下文之外可能会很糟糕/令人困惑,并且您无法诊断发生了什么。

方法 Promise.all 不会解决 Promise,当数组中的第一个 Promise 拒绝时,它会停止处理并拒绝。

还有 while 方法 batch仍然非常有用,因为它在处理值的方式上更加灵活,并且比 Promise.all 提供更好的结果/错误详细信息,现在不再需要使用它。它是在 ES5 时代开发的,当时 async/await 还不存在。但今天您可以轻松地将其替换为 async/await:

旧样式:

db.task('get-all-records', t => {
    return t.batch([
        t.any('SELECT * FROM apples'),
        t.any('SELECT * FROM oranges')
    ]);
})
    .then([apples, oranges] => {
        // process data here
    })
    .catch(error => {});

新风格:

const {apples, oranges} = await db.task('get-all-records', async t => {
    const apples = await t.any('SELECT * FROM apples');
    const oranges = await t.any('SELECT * FROM oranges');
    return {apples, oranges};
});

上面两个例子的结果是相同的,尽管它们在执行逻辑方面并不相同,因为第一个例子是完全异步的,而后者使用了async/await,这些是阻塞操作,如果之前的查询失败,它们甚至会阻止您创建下一个查询。

额外内容

执行多个独立查询(彼此不依赖)时,性能最佳的方法是将所有查询连接起来,并将它们作为一个查询执行。

为此,有方法 helpers.concat ,加上数据库方法multi ,处理多个结果:

const queries = [
    {query: 'SELECT * FROM apples WHERE color = $1', values: ['green']},
    'SELECT * FROM oranges'
];
const sql = pgp.helpers.concat(queries);

const [apples, oranges] = await db.multi(sql);

您甚至不需要事务,除非您的某些独立查询更改了数据。

关于pg-promise - 为什么我们应该使用batch()而不是Promise.all?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59265583/

相关文章:

node.js - 在 pg-promise 中跨多个 Controller 的多个查询之间共享事务或任务对象

postgresql - 带有 pg-promise 的 Postgres LISTEN/NOTIFY

node.js - 如何使 pg-promise 返回行作为数组?

javascript - 使用 Node.js 从其他文件调用方法

node.js - Express JS 对查询结果返回 null,并在使用 pgAdmin 查询时显示值

javascript - 测试微服务功能时模拟数据库

javascript - 在多行更新中使用 'skip' 选项

node.js - 使用 pg-promise 的嵌套事务

postgresql - 如何从数据库插入返回串行主键以用于另一个数据库插入

javascript - pgp.as.format() 中的“默认”选项