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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
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返回给上层进行统一处理。但,此时又报另一个异常:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
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

  • 1
(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……

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