Muljayan
Muljayan

Reputation: 3886

Database transactions happen parallely instead of sequentially in loop

I have an endpoint which loops through an array and updates the database as follows.

 app.post('/create', Authenticate, async (req, res) => {
  const {
    products,
  } = req.body;
  const trxProvider = knex.transactionProvider();
  const trx = await trxProvider();

  try {
    const formattedProduct = await Promise.all(products.map(async (product) => {

     // Get Current value
      const warehouseProducts = await trx('warehouse_products')
        .select('available_qty as qty')
        .where('product_code', product.product.code)
        .first();

      const finalQty = warehouseProducts.qty - product.orderQty;

     // Update database
      await trx('warehouse_products')
        .update({ available_qty: finalQty })
        .where('product_code', product.product.code);
    }));
    await trx('ordered_products')
      .insert(formattedProduct);
    trx.commit();
    console.log('Transaction successful');
    return send(res, 201, { success: true });
  } catch (err) {
    console.log(err);
    trx.rollback();
    const errors = {};
    errors.message = 'something went wrong';
    return send(res, 500, errors);
  }
});

The issue arises when i try to update the same row of the warehouse_products table within the loop. In the loop initially the qty value is taken from the warehouse_products table for a particular product then an arithmetic operation is done and the qty value is updated.

Ideally if both iterations access the same row, the second iteration's initial qty value should be what the first iteration updated. However the issue is that the second iteration too starts with the initial value of the first iteration. Its almost as if both iterations are happening parallel to each other instead of occurring sequentially.

Upvotes: 1

Views: 357

Answers (3)

arizafar
arizafar

Reputation: 3122

if you don't want to use an external library like Bluebird or Async

you can go with simple for a loop as following

let delay = (t) => new Promise((resolve) => {
	setTimeout(() => {
		return resolve(new Date().getMilliseconds())
	}, t*1000);
});

let array = [1,1,1,1];

//using simple for loop
async function test() {
	let out = [];
	for (let i = 0; i < 4; i++) {
		const res = await delay(array[i]);
		out.push(res);
	}
	return out;
}

test().then(res => {
	console.log(res)
})

Upvotes: 0

Kevin
Kevin

Reputation: 76

Have a look at the definition for Promise.all()

It is typically used after having started multiple asynchronous tasks to run concurrently and having created promises for their results, so that one can wait for all the tasks being finished.

Upvotes: 0

Ashish Modi
Ashish Modi

Reputation: 7770

Since you are using Promise.all it is supposed to happen in paralle. For sequential processing change this code

await Promise.all(products.map(async (product) => {
// logic here
});

to

for(const product of products) {
  // logic here
}

Upvotes: 2

Related Questions