序列化:findOne在事务期间自动提交(使用clsHooked)

时间:2019-02-05 13:49:05

标签: node.js transactions sequelize.js

我有类似以下的代码:

const _addEvents = async(eventList, day, transaction) => {
  await eventList.forEach(event => {
    return model.events.findOne({where:event, transaction: transaction}).then(event => {
        if(!event) {
          transaction.rollback();
          throw new Error("event not found");
        }

        return event;
      })
      .then(event => {
        day.addEvents(event, {through: {status: 'ENABLED'}});
        day.save();
      })
    })
  }
}

export const create = async(attributes) => {
        return await sequelize.transaction(async(t) => {
            return model.days.create(attributes, {transaction: t})).then(async(day) => {
                await _addEvents(attributes.eventList, day, t);
                return day;
            })
        })
}

我看到的是交易开始,并且在交易日内增加了一行。在第一次迭代中,在方法findOne中调用_addEvents时,将提交事务。

我使用过Sequelize.useCLS("foo");,所以我希望所有交易都在同一个命名空间中。 为了确定,我正在传递交易。

任何人都可以告诉我为什么在执行findOne之后提交事务,并告诉我如何避免这种情况的发生。

我正在使用(babel-core 6.26.3)和node.js v9.2.0&postgres 10.5

2 个答案:

答案 0 :(得分:0)

_addEvents正在执行异步工作,但是sequelize.transaction不知道需要等待它完成。 CLS负责将事务传递到嵌套上下文中,但是您仍然需要确保正在等待所有查询完成。

await _addEvents(attributes.eventList, day, t);

model.days.create中,您需要将事务作为第二个参数传递,请参见http://docs.sequelizejs.com/class/lib/model.js~Model.html#static-method-create

通常,我建议使用 CLS或将事务传递到使用它的任何地方。

答案 1 :(得分:0)

感谢@Jan Aagaard Meier&Co揭示问题。

我的问题是await eventList.forEach没有按照我的期望 做。

Array.prototype.forEach将返回undefined。它将返回与循环内的承诺状态无关的

因此之所以发生COMMIT,是因为在丢失的Promise(在循环内)可以完成之前,事务功能已经完成。

一种解决方案是使用bluebird's Promise.map将该行变成

await Promise.map(eventList, async(event) => {
    await model.events.findOne({where:event, transaction: transaction}).then(event => {
    ....
    }
});

这里地图将等到所有子Promise都完成,并且当我们返回Promise(并等待它)时,我们确保我们不会在Map完成之前完成交易。