如果订单很重要,交易中的每个 Knex 命令都必须仅在前一个命令的承诺得到履行后才提交吗?

Must each Knex command in a transaction be submitted only after the previous command's promise has been fulfilled if their order is important?

我正在使用第二种形式的 knex 交易(例如,'transacting')。但是,这个问题可能对第一种形式也有效。

文档示例显示:

// Using trx as a transaction object:
knex.transaction(function(trx) {

  const books = [
    {title: 'Canterbury Tales'},
    {title: 'Moby Dick'},
    {title: 'Hamlet'}
  ];

  knex.insert({name: 'Old Books'}, 'id')
    .into('catalogues')
    .transacting(trx)
    .then(function(ids) {
      books.forEach((book) => book.catalogue_id = ids[0]);
      return knex('books').insert(books).transacting(trx);
    })
    .then(trx.commit)
    .catch(trx.rollback);
})

两个 "insert" 命令正在事务中执行。第二个只有在第一个的 promise 完成后才提交(即在 promise 的 'then' 回调中)。

在上面的例子中这是必要的,因为只有在第一个命令的结果返回后才能制定第二个命令。

但是,也有可以预先制定多个命令的情况。因此,可以执行如下代码:

promise1 = knex('table1').insert(...).transacting(trx).then(...)
promise2 = knex('table2').insert(...).transacting(trx).then(...)

Use Promise.all to wait for completion and eventually commit or rollback.

实际上这段代码会执行。 Knex 会将命令排队并通过分配给事务的连接一次一个地发送出去。

但是,这就是我的问题的要点,将 knex 与 PostgreSQL 一起使用时的经验证据似乎表明命令发送到数据库的顺序不是保持。换句话说,命令提交给 knex 的顺序不一定是它们发送到数据库的顺序。似乎保证命令按特定顺序发送的唯一方法是创建一个承诺链,每个命令仅在前一个命令完成后才提交。

有人可以对此发表评论吗?这是设计使然吗?这可能是一个错误吗?

However, and this is the gist of my question, empirical evidence while using knex with PostgreSQL seems to show that the order in which the commands are sent to the database is not maintained. In other words, the order in which the commands were submitted to knex is not necessarily the order in which they are sent to the database. It seems that the only way to guarantee that commands are sent in a specific order is to create a promise chain whereby each command is submitted only after the previous command has been fulfilled.

听起来你的代码并没有按照你的想法去做。

Knex 按照您告诉它如何执行它们的顺序向数据库驱动程序发送查询(通过 awaiting 查询生成器或通过执行 .then() 方法)。

如果您对同一事务执行多个查询,则在较早完成之前 node-pg 驱动程序会缓冲第二个查询,并仅在第一个查询首先 return 编辑其响应后才发送它。

Use Promise.all to wait for completion and eventually commit or rollback.

如果没有抛出错误,这应该有效。

Promise.all([p1,p2fails,p3,p4,p5])

然而,如果 p2fails promise 抛出异常,那么 Promise.all 将立即拒绝并执行 rollback。之后 p3p4p5 仍然会尝试执行,因为它们已经缓冲在 node-pg 中以待发送。

会更安全,内存效率更高(查询存储两次,一次在 knex builder 中,一次作为 pg 驱动程序中的缓冲原始查询,带有大量插入,这可能很重要)并且更容易等待每个查询到 return 在发送下一个之前。在这里使用 Promise.all 甚至不会带来任何额外的并发优势,因为查询仍然通过相同的连接顺序执行。