通过knex迁移来承诺所有竞争条件

时间:2017-02-24 20:37:58

标签: javascript node.js race-condition knex.js

我被告知Promise.all解决其承诺的顺序无法保证。但是,我没有看到它从Promise.all native docs按顺序解析。

因此,以下knex迁移方法不应该起作用,因为messages具有对users表的引用。

然而,在几次迁移过程中,我从未碰到过一次实例,因为竞争条件存在错误。意思是,似乎Promise.all根据索引位置解析。

所以,我的问题是:以下片段是否容易出现竞争状况?

    return Promise.all([
      knex.schema.createTable('users', function(table) {
        table.increments().primary();
        ...
      }),
      knex.schema.createTable('messages', function(table) {
        table.increments().primary();
        table.bigInteger('user_id').unsigned().index()
          .references('id').inTable('users');
      }),

这是更好的方法吗?

    return Promise.all([
      knex.schema.createTable('users', function(table) {
        table.increments().primary();
        ...
      }),
    ]).then(function() {
      return Promise.all([
         knex.schema.createTable('messages', function(table) {
           table.increments().primary();
           table.bigInteger('user_id').unsigned().index()
             .references('id').inTable('users');
         }),
       });
   })

2 个答案:

答案 0 :(得分:1)

由于您有一个依赖项,因为您需要创建第一个表,然后才能在第二个表中引用它,您应该使用then - way。

Promise.all是否会看到其承诺是否按索引顺序解决,与Promise.all本身无关,而是将各个承诺作为参数传递给它。虽然您可以期望JavaScript(而不是Promise.all)按顺序评估参数列表,但您通常无法知道哪些承诺将首先解析。这取决于个人承诺,而不是Promise.all

在你的情况下,这些单独的promise会做类似的事情,即创建一个表,你的数据库引擎可能会按顺序处理这些语句,而不是并发,你可能在实践中看到promises总是按你列出的顺序解析他们,但依靠这一点是不好的做法。

请注意,如果您将仅包含一个承诺的数组传递给Promise.all,则可以跳过Promise.all并立即将then应用于该单一承诺。

答案 1 :(得分:0)

我希望此代码位于迁移文件中,默认情况下会创建隐式事务。因此,由于查询被发送到同一个连接,因此它们不会被并行解析。

在这种情况下,数据库驱动程序(至少pg)实际缓冲第二个查询并等待直到第一个查询被解析,然后再将第二个查询发送到数据库服务器。

唯一重要的是这两个查询以哪种顺序执行并发送到pg-driver。

由于Promise.all并不保证它会在knex.schema.createTable('users',...)之前执行knex.schema.createTable('messages',...)查询,这意味着理论上可能会出现第一个代码失败的情况。

tl; dr 第一种方法不容易出现争用情况,但如果查询执行顺序错误,则可能会失败。第二种方法更好。