In Nodejs (nestjs) there is a controller which handles incoming requests and calls the service function for the updation of the record. But sometimes the request is called for multiple times for the same data which results in the duplicity of the data.
The database used is MongoDB
// schema
// online payment
{
_id: ObjectId(),
txnId: string;
status: "success/ pending / failed",
response: {},
invoiceNumber: string,
}
//payment
{
_id: ObjectId(),
invoiceNumber:string;
onlinePaymentId: string;
... other params
}
Let's say the data is created once. When the update request (success /failure) hits the server it hits multiple times for the same data.
In service, I have added the functionality for updating the data status -> success / failed
When the status is a success, it results in creation on payment
document.
And update the invoiceNumber in onlinePaymentDocument.
I have added checks i.e. if the invoiceNumber exist in the online payment it will not create payment again.
But still, the payment
document is created for the same onlinePayment
document.
-- Next thing I tried is a single execution of the updateOnlinePayment function.
class PaymentService {
private lock = false;
// update payment
async update(data){
// all check for duplicity (invoice number already exist in document)
// find payment
// is invoice number exist
// if exist do nothing
if(this.lock){
// delay 1000 ms
return this.update(data)
}
this.lock = true;
// update online payment and create payment
// update invoice number in payment.
this.lock = false
return "online payment"
}
}
But still, the rate is reduced but not removed completely. Am I doing anything wrong? Is there any other method I can use to remove this type of parallel execution? or Any solution for this type of logic.
You need to run several operations atomically, that is:
There can be race conditions at each of these steps, and classical solution is database transaction.
Transaction allows running series of operations and if only storing to db if all goes well.
If not, all the changes are rolled back.
This is one of the ways use MongoDB transactions in nest.js:
@Injectable()
class PaymentService {
constructor(
// @InjectModel ...
@InjectConnection() private readonly connection: mongoose.Connection,
) {}
async update(data) {
const session = await this.connection.startSession();
session.startTransaction();
try {
// run all your business logic here
// that is check for invoice number
// find payment
// and update documents
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
session.endSession();
}
}