Home > front end >  Multiple Transactions in mysql for Node
Multiple Transactions in mysql for Node

Time:12-06

I'm using node's driver for mysql and need to execute 'n' number of transactions one after the other and not simultaneously.

I've tried using a for/forEach loop but the transactions seem to happen concurrently and that causes my api to crash.Here's the error :-

throw err; // Rethrow non-MySQL errors
      ^

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

One single transactions seems to work just fine.

Each transaction has 4 queries, req.body is an array of objects:-

router.post('/production/add', (req, res) => {
    for (const obj of req.body) {
        pool.getConnection(function (err, connection) {
            connection.beginTransaction(function (err) {
                if (err) throw err;

                const query1 = `select qty from production where prc_id = ${obj.prc_id}`;
                console.log(query1);
                connection.query(query1, function (error, result1, fields) {
                    if (error) {
                        return connection.rollback(function () {
                            res.status(400).send({ query: 1, message: error.sqlMessage, code: error.code, errno: error.errno });
                            return;
                        });
                    }
                    const new_prod_qty = result1[0].qty - obj.auth_prod_qty;
                    const query2 = new_prod_qty > 0 ? `update production set qty = ${new_prod_qty} where prc_id = ${obj.prc_id}` : `delete from production where prc_id = ${obj.prc_id}`;
                    console.log(query2);
                    connection.query(query2, function (error, results2, fields) {
                        if (error) {
                            return connection.rollback(function () {
                                res.status(400).send({ message: error.sqlMessage, code: error.code, errno: error.errno });
                                return;
                            });
                        }
                        const query3 = `update prc set auth_prod_qty = ${obj.auth_prod_qty} where prc_id = ${obj.prc_id}`;
                        console.log(query3);
                        connection.query(query3, function (error, results3, fields) {
                            if (error) {
                                return connection.rollback(function () {
                                    res.status(400).send({ message: error.sqlMessage, code: error.code, errno: error.errno });
                                    return;
                                });
                            }
                            const query4 = "select * from store";
                            connection.query(query4, function (error, results3, fields) {
                                if (error) {
                                    return connection.rollback(function () {
                                        res.status(400).send({ message: error.sqlMessage, code: error.code, errno: error.errno });
                                        return;
                                    });
                                }
                                connection.commit(function (err) {
                                    if (err) {
                                        return connection.rollback(function () {
                                            res.status(400).send({ message: error.sqlMessage, code: error.code, errno: error.errno });
                                            return;
                                        });
                                    }
                                    res.status(201).send(results2);
                                });
                            });
                        });
                    });
                });
            });
        });
    };
});

Based off some research Sequelize ORM seems to promisify transactions but however I'm hoping to use it as a last resort. Any sort of solution with or without Sequelize would be appreciated!

Thanks in advance!

Solution:-

router.post('/stock/add', async (req, res) => {
    const connection = pool.getConnection(async function (err, connection) {
        if (err) {
            connection.release();
            res.status(400).send(err);
            return;
        }
        else {
            for (const obj of req.body) {
                try {
                    await connection.promise().beginTransaction();
                    const [result1, fields1] = await connection.promise().query(query1)
                    const [result2, fields2] = await connection.promise().query(query2);
                    const [result3, fields3] = await connection.promise().query(query3);
                    const [result4, fields4] = await connection.promise().query(query4);
                    await connection.promise().commit();
                }
                catch (error) {
                    await connection.promise().rollback();
                    connection.release();
                    res.status(400).send(error);
                    return;
                }
            }
            res.status(200).send('Transaction Complete');
        }
    });
});

CodePudding user response:

You need to use async / await to run your txs sequentially. How to do this?

Use npm mysql2 in place of npm mysql. That gets you promisified (awaitable) versions of the APIs when you require('mysql2/promise'). Plus, this is much more fun to program and debug than those miserable nested callbacks. Just don't forget the awaits.

Use this basic outline for your code's data processing loop. Everything will go in order sequentially. The way you create your pool is a little different; read the npm page. This is not debugged.

const mysql = require('mysql2/promise');

router.post('/production/add', async (req, res) => {
  const connection = await pool.getConnection()

  for (const obj of req.body) {
    try {
      await connection.beginTransaction()
      const query1 = 'whatever'
      const result1 = await connection.query(query1)
      const query2 = 'something else'
      const result 2 = await connection.query(query2)
      /* etcetera etcetera */
      await connection.commit()
    } 
    catch (error) {
      await connection.rollback()
      pool.releaseConnection()
      res.status(400).send({ something })
    }
  }
  pool.releaseConnection()
}
  • Related