Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> MongoDB

การจำลอง Transaction Lock ใน MongoDB 4.0 ด้วย findOneAndUpdate

ใช้ findOneAndUpdate() แทน Transaction ใน MongoDB 4.0

MongoDB 4.0 ยังไม่รองรับ Multi-document Transaction (รองรับตั้งแต่ 4.0 สำหรับ Replica Set และ 4.2 สำหรับ Sharded Cluster) สำหรับกรณีที่ต้องการ Lock เอกสารเพื่อป้องกัน Race Condition สามารถใช้ findOneAndUpdate() ซึ่งทำงานแบบ Atomic

ตัวอย่างข้อมูลทดสอบ

> db.demo404.insertOne({ "FirstName": "John" });
> db.demo404.insertOne({ "FirstName": "Robert" });
> db.demo404.insertOne({ "FirstName": "Mike" });

แสดงข้อมูลทั้งหมด

> db.demo404.find();

ผลลัพธ์:

{ "_id": ObjectId("..."), "FirstName": "John" }
{ "_id": ObjectId("..."), "FirstName": "Robert" }
{ "_id": ObjectId("..."), "FirstName": "Mike" }

คำสั่ง Lock เอกสารแรกที่ยังไม่มี in_transaction

> result = db.demo404.findOneAndUpdate(
  { "in_transaction": { $exists: false } },
  { $set: { "in_transaction": true } }
);

ผลลัพธ์ที่ได้

{ "_id": ObjectId("5e6f8c38fac4d418a0178592"), "FirstName": "John" }

คำอธิบาย

  • findOneAndUpdate(): ค้นหาเอกสาร 1 รายการ อัปเดต และคืนค่าเอกสารเดิม (ก่อนอัปเดต) พร้อมกันแบบ Atomic
  • { "in_transaction": { $exists: false } }: หาเอกสารที่ยังไม่มีฟิลด์ in_transaction (ยังไม่ถูก lock)
  • { $set: { "in_transaction": true } }: ตั้งค่า in_transaction = true เพื่อ lock

รูปแบบการใช้งาน Lock Pattern

  1. พยายาม Lock: findOneAndUpdate({ in_transaction: { $exists: false } }, { $set: { in_transaction: true, lockedBy: "processId", lockedAt: new Date() } })
  2. ถ้าสำเร็จ (คืนเอกสารไม่ใช่ null) -> ทำงานต่อ
  3. เสร็จสิ้น -> updateOne({ _id: doc._id }, { $unset: { in_transaction: "", lockedBy: "", lockedAt: "" } }) เพื่อปลด Lock
  4. ถ้าล้มเหลว (คืน null) -> รอหรือลองใหม่

ข้อจำกัด

  • ทำงานได้เฉพาะ Single Document เท่านั้น
  • ต้องจัดการ Timeout/Deadlock เอง (เช่น TTL Index บน lockedAt)
  • สำหรับ Multi-document Transaction จริง ให้อัปเกรด MongoDB 4.0+ (Replica Set) หรือ 4.2+ (Sharded)