question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Show warning when query inside transaction block is not using trx

See original GitHub issue

I have had quite a few occasions where I’ve created a transaction block but I forget to use the trxobject and it causes errors in my app logic. For example,

function insertAndReturnUsers(userObj) {
  return knex.transaction(trx => {
    return trx.insert(userObj).into('table_a')
      .then(() => getUsers())
  })
  .catch(err => {
    // Transaction failed.. do something
  });
}


function getUsers() {
  return knex('users').select('*');
}

In this case insertAndReturnUsers function has a bug. It doesn’t return the newly created user when listing all users. I tend to do this mistake a lot and almost never my intention is to do a query outside the transaction.

Could it be possible to add a developer warning when accidentally doing this ?

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:12 (3 by maintainers)

github_iconTop GitHub Comments

4reactions
tgriessercommented, Jun 3, 2016

I’ve thought about this (and I’m interested to hear any other ideas here). There isn’t really a simple way to do this, since it’s possible to lift the transaction object out of the transaction block.

Instead I’m working on adding a new path for transactions, keeping but likely eventually deprecating the current syntax, where you’d instead call as a function and receive a transaction object:

const trx = knex.transaction(/* optional config */)

trx.insert(userObj).into('table_a')
  .then(() => trx.select('*').from('users'))
  .then(trx.commit)
  .catch(trx.rollback)

this would make it simpler to create a transaction which properly works across an entire request / response cycle:

app.use((req, res, next) => {
  req.knex = knex.transaction()
  onHeaders(req, () => {
    if (!req.knex.complete) {
      console.warn('Transaction was not committed or rolled back')
      req.knex.commit()
    }
  })
  next()
})
app.use((err, req, res, next) => {
  if (req.knex && !req.knex.complete) {
    req.knex.rollback(err)
  }
  //...
})

Then you’d just be sure to use req.knex and every query would always be on the same connection / transaction. It would also be possible to limit this behavior to post requests if you want.

app.use((req, res, next) => {
  if (req.method === 'POST' || req.method === 'PUT' || req.method === 'DELETE') {
    req.knex = knex.transaction()
    onHeaders(req, () => {
      if (!req.knex.complete) {
        console.warn('Transaction was not committed or rolled back')
        req.knex.commit()
      }
    })
  } else {
    req.knex = knex
  }
  next()
})

Finally, these transaction objects would be lazy / inexpensive, there wouldn’t actually be any connection checked out of the pool and no queries would be sent to the database until the first query is executed.

0reactions
kibertoadcommented, May 26, 2020

@timbrownsf I think transactionProvider might be a helpful solution for you. Check out this example from documentation:

// Does not start a transaction yet
const trxProvider = knex.transactionProvider();

const books = [
  {title: 'Canterbury Tales'},
  {title: 'Moby Dick'},
  {title: 'Hamlet'}
];

// Starts a transaction
const trx = await trxProvider();
const ids = await trx('catalogues')
  .insert({name: 'Old Books'}, 'id')
books.forEach((book) => book.catalogue_id = ids[0]);
await  trx('books').insert(books);

// Reuses same transaction
const sameTrx = await trxProvider();
const ids2 = await sameTrx('catalogues')
  .insert({name: 'New Books'}, 'id')
books.forEach((book) => book.catalogue_id = ids2[0]);
await sameTrx('books').insert(books);
Read more comments on GitHub >

github_iconTop Results From Across the Web

Knex transaction Promises warnings - Stack Overflow
Let me try to simplify: query(username, sql, params, callback) { this.pgKnex.transaction((trx) => { return this.
Read more >
14.16.2.1 Using InnoDB Transaction and Locking Information
In this scenario, use the following query to see which transactions are waiting and which transactions are blocking them: SELECT r.trx_id waiting_trx_id, ...
Read more >
How to Fix a Lock Wait Timeout Exceeded Error in MySQL
The offensive transaction is not fast enough to commit or rollback the transaction within innodb_lock_wait_timeout duration.
Read more >
SHOW INNODB STATUS walk-through - Percona
To start with basics, SHOW INNODB STATUS is a command which prints out a lot of internal Innodb performance counters, statistics, information ...
Read more >
MariaDB Transactions and Isolation Levels for SQL Server ...
It is possible to write into transactional and non-transactional tables within a single transaction. It is important to remember that ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found