解决并行保存到 mongodb
Solve parallel saves to mongodb
所以我最近尝试创建一个预订网站,您可以在其中 select 您想要预订的日期。然后您将向 /api/v1/reserve 发送一个请求,该请求将检查数据是否有效。生成条带支付意向,将预订保存到数据库,并将支付意向id发送给前端(确认)。我的问题是,如果两个客户同时点击发送,他们都会为同一天付款,这是不应该发生的。
router.post("/reservation", async (req, res) => {
//Check if everything is in the reqest
if (!req.body?.name || !req.body?.phone || !req.body?.date) return res.sendStatus(400);
if (!req.body.phone.match(/^[+]{0,1}[4]{1,2}[8][\s0-9]*$/)) return res.status(400).json({
type: ErrorTypes.PHONE
});
//Validating date
const reservationDate = new Date(req.body.date);
if (reservationDate < new Date()) return res.status(400).json({
type: ErrorTypes.DATE
});
//Checking if date is already reserved
if (await checkIfDateReserved(reservationDate)) return res.status(400).json({
type: ErrorTypes.RESERVED
})
//if reservation date is weekend raise the price
const estimatedPrice = reservationDate.getDay() == 6 || reservationDate.getDay() == 0 ? 3500 : 2000;
try {
//Create payment intet
const intent = await stripe.paymentIntents.create({
amount: estimatedPrice,
currency: "PLN",
payment_method_types: ['card']
});
const document = new ReservationModel({
name: req.body.name,
reservationDate: reservationDate,
phone: req.body.phone,
complete: false,
intentId: intent.id
});
await document.save();
//If everything went smoothley, send the secret to the client
//Send the client the intent secret to confirm the payment
res.json({ clientSecret: intent.client_secret });
} catch (error) {
res.status(500);
}
});
基本上,两个进程 运行 在不同的线程中并行,它们检查日期是否同时保留并同时保留。我怎样才能让数据库等到第一个保存请求完成后再处理另一个? reservationDate 字段是唯一的,但似乎 MongoDB 仍然忽略它。
PS: 另外,我考虑过使用事务,但我真的不知道它们在这里如何应用。
所以我最终使用了 question thanks to 0x1C1B 中的答案。互斥量最终完美地工作。如果有人需要,请留下代码。
const mutex = new Mutex();
router.post("/reservation", async (req, res) => {
//Check if everything is in the reqest
if (!req.body?.name || !req.body?.phone || !req.body?.date) return res.sendStatus(400);
if (!req.body.phone.match(/^[\s0-9]{9,11}/)) return res.status(400).json({
type: ErrorTypes.PHONE
});
//Validating date
const reservationDate = new Date(req.body.date);
if (reservationDate < new Date()) return res.status(400).json({
type: ErrorTypes.DATE
});
await mutex.lock(); //Lock the mutex to avoid processing two reservations at once
//Checking if date is already reserved
if (await checkIfDateReserved(reservationDate)) {
mutex.release(); //Release the mutex so other requests can continue
return res.status(400).json({
type: ErrorTypes.RESERVED
})
}
//if reservation date is weekend raise the price
const estimatedPrice = reservationDate.getDay() == 6 || reservationDate.getDay() == 0 ? 3500 : 2000;
try {
//Create payment intet
const intent = await stripe.paymentIntents.create({
amount: estimatedPrice,
currency: "PLN",
payment_method_types: ['card']
});
const document = new ReservationModel({
name: req.body.name,
reservationDate: reservationDate,
phone: req.body.phone,
complete: false,
intentId: intent.id
});
await document.save();
//If everything went smoothley, send the secret to the client
//Send the client the intent secret to confirm the payment
res.json({ clientSecret: intent.client_secret }); //Send the client_secret required for payment confirmation
//If the confirmation doesn't go through in 5 minutes the reservation is canceled due to "createdAt" field in the schema (see ../../schemas/Reservation.ts)
//If the payment goes through the automatic deletion is cancelled (see index.ts:59)
mutex.release(); //Release mutex after reservation has been saved to db
//So other users can't reserve it
} catch (error) {
res.status(500);
mutex.release();
}
});
所以我最近尝试创建一个预订网站,您可以在其中 select 您想要预订的日期。然后您将向 /api/v1/reserve 发送一个请求,该请求将检查数据是否有效。生成条带支付意向,将预订保存到数据库,并将支付意向id发送给前端(确认)。我的问题是,如果两个客户同时点击发送,他们都会为同一天付款,这是不应该发生的。
router.post("/reservation", async (req, res) => {
//Check if everything is in the reqest
if (!req.body?.name || !req.body?.phone || !req.body?.date) return res.sendStatus(400);
if (!req.body.phone.match(/^[+]{0,1}[4]{1,2}[8][\s0-9]*$/)) return res.status(400).json({
type: ErrorTypes.PHONE
});
//Validating date
const reservationDate = new Date(req.body.date);
if (reservationDate < new Date()) return res.status(400).json({
type: ErrorTypes.DATE
});
//Checking if date is already reserved
if (await checkIfDateReserved(reservationDate)) return res.status(400).json({
type: ErrorTypes.RESERVED
})
//if reservation date is weekend raise the price
const estimatedPrice = reservationDate.getDay() == 6 || reservationDate.getDay() == 0 ? 3500 : 2000;
try {
//Create payment intet
const intent = await stripe.paymentIntents.create({
amount: estimatedPrice,
currency: "PLN",
payment_method_types: ['card']
});
const document = new ReservationModel({
name: req.body.name,
reservationDate: reservationDate,
phone: req.body.phone,
complete: false,
intentId: intent.id
});
await document.save();
//If everything went smoothley, send the secret to the client
//Send the client the intent secret to confirm the payment
res.json({ clientSecret: intent.client_secret });
} catch (error) {
res.status(500);
}
});
基本上,两个进程 运行 在不同的线程中并行,它们检查日期是否同时保留并同时保留。我怎样才能让数据库等到第一个保存请求完成后再处理另一个? reservationDate 字段是唯一的,但似乎 MongoDB 仍然忽略它。
PS: 另外,我考虑过使用事务,但我真的不知道它们在这里如何应用。
所以我最终使用了
const mutex = new Mutex();
router.post("/reservation", async (req, res) => {
//Check if everything is in the reqest
if (!req.body?.name || !req.body?.phone || !req.body?.date) return res.sendStatus(400);
if (!req.body.phone.match(/^[\s0-9]{9,11}/)) return res.status(400).json({
type: ErrorTypes.PHONE
});
//Validating date
const reservationDate = new Date(req.body.date);
if (reservationDate < new Date()) return res.status(400).json({
type: ErrorTypes.DATE
});
await mutex.lock(); //Lock the mutex to avoid processing two reservations at once
//Checking if date is already reserved
if (await checkIfDateReserved(reservationDate)) {
mutex.release(); //Release the mutex so other requests can continue
return res.status(400).json({
type: ErrorTypes.RESERVED
})
}
//if reservation date is weekend raise the price
const estimatedPrice = reservationDate.getDay() == 6 || reservationDate.getDay() == 0 ? 3500 : 2000;
try {
//Create payment intet
const intent = await stripe.paymentIntents.create({
amount: estimatedPrice,
currency: "PLN",
payment_method_types: ['card']
});
const document = new ReservationModel({
name: req.body.name,
reservationDate: reservationDate,
phone: req.body.phone,
complete: false,
intentId: intent.id
});
await document.save();
//If everything went smoothley, send the secret to the client
//Send the client the intent secret to confirm the payment
res.json({ clientSecret: intent.client_secret }); //Send the client_secret required for payment confirmation
//If the confirmation doesn't go through in 5 minutes the reservation is canceled due to "createdAt" field in the schema (see ../../schemas/Reservation.ts)
//If the payment goes through the automatic deletion is cancelled (see index.ts:59)
mutex.release(); //Release mutex after reservation has been saved to db
//So other users can't reserve it
} catch (error) {
res.status(500);
mutex.release();
}
});