Skip to content

Distributed locking

โดยทั่วไปแล้ว resource บางอย่างในระบบ ไม่ควรจะถูก access ได้พร้อมกันโดยหลาย process ไม่งั้นอาจจะทำให้เกิด race condition ได้ ยกตัวอย่างเช่น record ใน database เรายอมให้ 1 record ถูกอ่านได้พร้อมกันในหลายๆ process แต่โดยปกติเราจะไม่ยอมให้ record ถูก update ได้พร้อมๆกัน

ตัวอย่าง Bob และ Alice ต้องการจะเพิ่มค่าของตัวแปร count แต่ process ดันเข้ามา access ตัวแปร count ได้พร้อมๆกัน

uml diagram

ค่าสุดท้ายของ count คือ 20 ซึ่งไม่ถูก จริงๆควรจะเป็น 25

จริงๆแล้วปัญหานี้แก้ได้ไม่ยากด้วยการใช้ database transaction ซึ่งมันจะจัดการ concurrency control ให้เราได้ ด้วย mechanic ต่างๆ เช่น optimistic lock, pessimistic lock

แล้วถ้า resource ของเราไม่ได้อยู่ใน database ล่ะ เช่นอาจจะเป็น file ที่ถูกเก็บอยู่ใน blob storage ซักที่นึง แล้วดันมีหลาย service ที่ต้องการเข้ามาอ่านและ update content ของ file นี้พร้อมๆกัน

uml diagram

content สุดท้ายของ a.txt คือ abcabc ซึ่งไม่ถูก จริงๆควรจะเป็น abcabcabc

ตอนนี้เราไม่สามารถโยน concurrency control ไปให้ database จัดการให้เราได้แล้ว ดังนั้นเราต้องจัดการ concurrency control ใน application layer แทนที่จะเป็น database layer

โดยไอเดียของ distributed locking คือเราจะมี global lock ตัวนึง ที่สามารถเข้าถึงได้โดยทุก service ทำหน้าที่ lock resource ที่เราไม่อยากให้ถูก access ได้พร้อมๆกันโดยหลาย service

ตัว global lock ของเราจะ provide 2 operations คือ AcquireLock และ ReleaseLock

  • AcquireLock รับ parameter key เป็นตัวอ้างอิงว่าเรากำลังจะ lock resource ไหน เมื่อ resource ถูก lock อยู่ จะไม่มี service ไหนเข้ามา acquire lock ด้วย key นั้นๆได้อีก โดยจำเป็นจะต้องรอให้ service ก่อนหน้า ReleaseLock ก่อน (อาจจะทำ polling จนกว่าจะ AcquireLock ได้สำเร็จ)
  • ReleaseLock รับ parameter key เป็นตัวอ้างอิงว่าเราจะ release lock ของ resource ไหน
uml diagram

จะเห็นว่าเพียงเท่านี้ content สุดท้ายของ a.txt นั้นถูกต้องแล้ว

โดยปกติแล้ว global lock จะถูก implement ด้วย in-memory database เช่น redis เพื่อให้ได้ performance ที่ดี อย่างไรก็ตามสังเกตว่า เรากำลังบังคับ concurrent process ให้กลายเป็น sequential process อยู่ หมายความว่าถ้าระบบของเราใช้ distributed lock มากเกินไป ก็อาจจะเกิด performance issue อยู่ดี อีกทั้ง global lock อาจจะกลายเป็น single point of failure ในระบบ

สำหรับตัวอย่าง implementation ของ distributed lock สามารถอ่านต่อได้ใน documentation ของ redis ซึ่งจะเป็นการนำ redis มา apply เป็น global lock และยังมี opensource library พร้อมใช้งาน รองรับหลายภาษา