简体   繁体   中英

Nodejs race condition for database insert and update

I am using Nodejs with SQL Server database. I am using this node-mssql package for writing queries from Nodejs.

I have a route which has a if condition and query structure as below:

let checkPartExists = await pool.query(`Select * from Parts WHERE partID = ${partID}`);
if (checkPartExists.recordset.length == 0){
   await pool.query(`INSERT INTO Parts(PartID, Quantity) VALUES(${partID}, ${quantity})`)
}
else{
   await pool.query(`UPDATE Parts SET Quantity = ${quantity} WHERE PartID = ${partID}`)
}

Now, if the single threaded Nodejs didn't have any event loop, I could safely assume that this would always work. But I know that that is not the case. I just had an instance where the same partID has been inserted twice.

My understanding is that:

  1. User 1 makes a post request to that route
  2. It executes the select query, finds that this partID does not exist in the parts table and reaches the insert portion
  3. However, before it finishes with the insert User 2 (or maybe the same user) makes a post request and the select query is executed which also thinks that a part with that partID does not exist.

This will insert the same partID twice. Is this called a race condition? How do I prevent such situation?

I know I can make PartID a unique key in the database and throw an error when this happens, but I feel like there has to be a way of handling this through code as well.

Please let me know how you guys/girls are handling such situations.

This is a job for a Transaction . Something like this.

const transaction = new sql.Transaction()
await transaction.begin()
const request = new sql.Reqest(transaction)
let checkPartExists = await request.query(`
     Select * from Parts WHERE partID = ${partID}`);
if (checkPartExists.recordset.length == 0){
   await request.query(`INSERT INTO Parts(PartID, Quantity) VALUES(${partID}, ${quantity})`)
} else {
   await request.query(`UPDATE Parts SET Quantity = ${quantity} WHERE PartID = ${partID}`)
}
await transaction.commit()

This serializes access to the table. Transactions are the standard way of avoiding race conditions.

If only one cluster of node.js application is running, then async-mutex can be the solution. If you are running multiple clusters then distributed deadlocking could be a solution.

Mutex is a design pattern so the resource can not be shared among instances.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM