Unable to catch knex transaction rejection

2019-09-21 02:49发布

问题:

I'm using knex transaction with async/await syntax as suggested in this question: Get Knex.js transactions working with ES7 async/await

My problem is, that when transaction fails and trx callback is invoked, knex logs

Unhandled rejection error: relation "some_table" doesn't exist // Example error which I used for testing

just under the same error logged by logger, so logs looks like that:

// Removed error stacks...

// Error logged by logger
2019-07-14T23:12:29.606Z [error]: error: insert into "tab1" ("col1", "col2", "col3") values ($1, $2, $3) returning "col3" - relation "tab1" does not exist

// Koa.js error from ctx.throw()
InternalServerError: Internal Server Error

// Error when invoking await trx.rollback(e)
Unhandled rejection error: relation "tab1" does not exist

What I want to achive is to call trx.rollback(e) without throwing unhandled rejection error.

And the code causing this problem:

async function create (ctx) {
  const trx = await tools.promisify(knex.transaction.bind(knex))
  try {
    let [var1] = await trx('tab1').insert({...}).returning(['x', 'y'])

    // tab2 doesn't exist to trigger an error
    const [var2] = await trx('tab2').insert({...}).returning('z')

    await trx.commit()
  } catch (e) {
    await trx.rollback(e)
    logger.error(e)
    ctx.throw()
  }
}

回答1:

You are using transactions wrong... try this:

async function create (ctx) {
  try {
    const res = await knex.transaction(async trx => {
      let [var1] = await trx('tab1').insert({...}).returning(['x', 'y'])
      // tab2 doesn't exist to trigger an error
      const [var2] = await trx('tab2').insert({...}).returning('z')

      return [var1, var2];
    });
  } catch (e) {
    logger.error(e)
    ctx.throw()
  }
}

Also if you really want you can use latest knex 0.18 and transactionProvider()... it is found from knex docuementation... but in your case 1st way works even better and will be more robust that explicit commit.

Real answer to your question is that the line:

trx = await tools.promisify(knex.transaction.bind(knex))

doesn't bind any handlers to promise chain returned by knex.transaction(trx => {...}) call and then when you call .rollback(e) that chain rejects and leaks that exception.