当前位置: 首页 → 技术圈 → 

Node.js

Sequelize中的事务操作及异常处理

 

近日,遇到一事务问题,在执行create时报以下错误:

Unhandled rejection SequelizeUniqueConstraintError: Validation error

    at Query.formatError (/Users/fuyun/.../node_modules/sequelize/lib/dialects/mysql/query.js:223:16)

    at Execute.handler [as onResult] (/Users/fuyun/.../node_modules/sequelize/lib/dialects/mysql/query.js:51:23)

    at Execute.execute (/Users/fuyun/.../node_modules/mysql2/lib/commands/command.js:30:14)

    at Connection.handlePacket (/Users/fuyun/.../node_modules/mysql2/lib/connection.js:417:32)

    at PacketParser.onPacket (/Users/fuyun/.../node_modules/mysql2/lib/connection.js:75:12)

    at PacketParser.executeStart (/Users/fuyun/.../node_modules/mysql2/lib/packet_parser.js:75:16)

    at Socket.<anonymous> (/Users/fuyun/.../node_modules/mysql2/lib/connection.js:82:25)

    at Socket.emit (events.js:223:5)

    at Socket.EventEmitter.emit (domain.js:498:23)

    at addChunk (_stream_readable.js:309:12)

    at readableAddChunk (_stream_readable.js:290:11)

    at Socket.Readable.push (_stream_readable.js:224:10)

    at TCP.onStreamRead (internal/stream_base_commons.js:181:23)

查看日志后发现问题出在主键重复了,便引入异常处理,将error抛出,再通过reject返回给上层进行统一处理。但,此时又报另一个异常:

Unhandled rejection Error: rollback has been called on this transaction(639ce933-3246-484d-9183-699093ee4cbd), you can no longer use it. (The rejected query is attached as the 'sql' property of this error)

    at checkTransaction (/Users/fuyun/.../node_modules/sequelize/lib/sequelize.js:623:25)

    at /Users/fuyun/.../node_modules/sequelize/lib/sequelize.js:636:9

    at tryCatcher (/Users/fuyun/.../node_modules/sequelize/node_modules/bluebird/js/release/util.js:16:23)

    at Function.Promise.attempt.Promise.try (/Users/fuyun/.../node_modules/sequelize/node_modules/bluebird/js/release/method.js:39:29)

    at /Users/fuyun/.../node_modules/sequelize/lib/sequelize.js:631:53

    at /Users/fuyun/.../node_modules/retry-as-promised/index.js:70:21

    at new Promise (<anonymous>)

    at retryAsPromised (/Users/fuyun/.../node_modules/retry-as-promised/index.js:60:10)

    at /Users/fuyun/.../node_modules/sequelize/lib/sequelize.js:631:30

    at tryCatcher (/Users/fuyun/.../node_modules/sequelize/node_modules/bluebird/js/release/util.js:16:23)

    at Function.Promise.attempt.Promise.try (/Users/fuyun/.../node_modules/sequelize/node_modules/bluebird/js/release/method.js:39:29)

    at Sequelize.query (/Users/fuyun/.../node_modules/sequelize/lib/sequelize.js:580:23)

    at QueryInterface.insert (/Users/fuyun/.../node_modules/sequelize/lib/query-interface.js:885:27)

    at /Users/fuyun/.../node_modules/sequelize/lib/model.js:3987:52

    at bound (domain.js:419:14)

    at runBound (domain.js:432:12)

    at tryCatcher (/Users/fuyun/.../node_modules/sequelize/node_modules/bluebird/js/release/util.js:16:23)

    at Promise._settlePromiseFromHandler (/Users/fuyun/.../node_modules/sequelize/node_modules/bluebird/js/release/promise.js:547:31)

    at Promise._settlePromise (/Users/fuyun/.../node_modules/sequelize/node_modules/bluebird/js/release/promise.js:604:18)

    at Promise._settlePromise0 (/Users/fuyun/.../node_modules/sequelize/node_modules/bluebird/js/release/promise.js:649:10)

    at Promise._settlePromises (/Users/fuyun/.../node_modules/sequelize/node_modules/bluebird/js/release/promise.js:729:18)

    at _drainQueueStep (/Users/fuyun/.../node_modules/sequelize/node_modules/bluebird/js/release/async.js:93:12)

    at _drainQueue (/Users/fuyun/.../node_modules/sequelize/node_modules/bluebird/js/release/async.js:86:9)

    at Async._drainQueues (/Users/fuyun/.../node_modules/sequelize/node_modules/bluebird/js/release/async.js:102:5)

    at Immediate.Async.drainQueues [as _onImmediate] (/Users/fuyun/.../node_modules/sequelize/node_modules/bluebird/js/release/async.js:15:14)

    at processImmediate (internal/timers.js:439:21)

    at process.topLevelDomainCallback (domain.js:130:23)

同时,还有一个warning

(node:48615) [DEP0097] DeprecationWarning: Using a domain property in MakeCallback is deprecated. Use the async_context variant of MakeCallback or the AsyncResource class instead.

网上搜了一圈,并没有找到解决方案,只找到一些关于Promise的异常处理的文章,于是试着加上try {...} catch {...},问题依旧。

而后将models.sequelize.transaction()层的.then(onFulfill, onReject)改为.then(onFulfill).catch(onError),依然无法解决。

甚至,因为关于domain的那个warning ,而琢磨是否domain的问题?于是又对照了一番node.js的官方API,发现除去deprecated的警告外,用法并没有错误。看来,问题还是出在Promise上。

又琢磨了半天,偶然发现model.create()的结果处理中遗漏了reject和catch部分,于是分别在事务的几个子查询中都加上了异常处理逻辑。结果完美验证了所想。只是,虽然解决了error问题,但上面那个醒目的warning依然存在……

毫无头绪,既然都已经捕获了,也不再bubble了,结果也返回到页面了,为何还会出现warning?必然是在某个环节触发了domain机制。无解ing……

此问题也说明了,异步操作中的异常处理是何等复杂,如幽灵一般,难以捉摸……


公众号:黑匣子思维
 

*

*

* 验证码

*