Optimistic Locking #
Dalam sistem backend modern, concurrency adalah keniscayaan. Multiple request, worker, atau service bisa mengakses dan memodifikasi data yang sama secara bersamaan. Untuk menjaga data consistency, kita mengenal dua pendekatan utama: pessimistic locking dan optimistic locking.
Optimistic locking sering dianggap “lebih aman” dan “lebih scalable” karena tidak melakukan lock sejak awal. Namun, penggunaan yang keliru justru bisa membebani database, menurunkan throughput, bahkan menyebabkan bottleneck tersembunyi.
Artikel ini membahas:
- Apa itu optimistic locking
- Cara kerjanya
- Kesalahan umum (termasuk penyalahgunaan
SELECT ... FOR UPDATE) - Dampaknya terhadap database
- Best practice penggunaan optimistic locking
Apa Itu Optimistic Locking? #
Optimistic locking adalah strategi concurrency control yang berasumsi konflik jarang terjadi.
Alih-alih mengunci data sejak dibaca, sistem:
- Membaca data beserta version / revision / updated_at
- Melakukan perubahan di aplikasi
- Saat
UPDATE, database akan memverifikasi apakah data belum berubah sejak terakhir dibaca
Jika data sudah berubah → update gagal → retry atau abort.
Contoh Konsep Sederhana #
UPDATE orders
SET status = 'PAID', version = version + 1
WHERE id = 10 AND version = 3;
Jika rows_affected = 0, artinya:
- Ada transaksi lain yang lebih dulu mengubah data
- Versi sudah tidak cocok
Ini inti dari optimistic locking.
Kenapa Optimistic Locking Dianggap Lebih Scalable? #
Karena:
- Tidak menahan lock lama
- Tidak memblokir reader lain
- Cocok untuk sistem high read, low write conflict
Namun… ini hanya benar jika digunakan dengan benar.
Kesalahan Fatal: Menggabungkan Optimistic Locking dengan FOR UPDATE
#
Salah satu kesalahan paling sering terjadi:
Mengklaim menggunakan optimistic locking, tetapi tetap memakai
SELECT ... FOR UPDATE
Contoh Pola yang Salah #
BEGIN;
SELECT * FROM orders WHERE id = 10 FOR UPDATE;
-- logic bisnis berat (API call, loop, validasi kompleks)
UPDATE orders SET status = 'PAID' WHERE id = 10;
COMMIT;
Ini BUKAN optimistic locking, melainkan pessimistic locking terselubung.
Dampak Buruk FOR UPDATE dengan Logic Lama
#
Jika logic di dalam transaction lama (misalnya):
- Validasi kompleks
- Hit API eksternal
- Loop data besar
- Menunggu resource lain
Maka dampaknya:
1. Lock Ditahan Terlalu Lama #
- Row tetap terkunci
- Transaksi lain menunggu
2. Lock Queue Menumpuk #
- Request berikutnya tidak gagal
- Tapi menunggu diam-diam
3. Throughput Turun Drastis #
- Database terlihat “normal”
- Tapi latency meningkat
4. Potensi Deadlock #
- Terutama jika banyak row dan urutan akses berbeda
5. Efek Domino ke Seluruh Sistem #
- Thread pool aplikasi habis
- Connection pool DB penuh
- API timeout
Ironisnya, semua ini sering tidak terlihat di awal.
Optimistic Locking yang Benar (Tanpa Lock Panjang) #
Pola yang benar:
1. Read Tanpa Lock #
SELECT id, status, version FROM orders WHERE id = 10;
2. Logic di Aplikasi (Tanpa Transaction DB) #
- Validasi status
- Hit service lain
- Hitung nilai
3. Atomic Update dengan Version Check #
UPDATE orders
SET status = 'PAID', version = version + 1
WHERE id = 10 AND version = 3;
4. Cek Rows Affected #
1→ sukses0→ konflik (retry / return error)
Database hanya bekerja sangat singkat dan atomic.
Kenapa Optimistic Locking Bisa Membebani DB Jika Salah Pakai? #
1. Retry Tanpa Kontrol #
- Banyak retry tanpa backoff
- Menyebabkan write storm
2. Version Column Tidak Di-index #
- Update berubah jadi full scan
3. Update Terlalu Sering Gagal #
- Konflik tinggi
- CPU DB naik
4. Logic Bisnis Tidak Idempotent #
- Retry menyebabkan side effect ganda
Kapan Optimistic Locking Cocok Digunakan? #
Gunakan optimistic locking jika:
- Konflik jarang
- Data sering dibaca
- Update singkat dan atomic
- Bisa menerima retry
Contoh:
- Update profile user
- Status order
- Metadata
Hindari jika:
- Konflik tinggi
- Update batch besar
- Butuh konsistensi kuat lintas banyak row
Best Practice Optimistic Locking #
1. Gunakan Kolom Version Khusus #
version INT- atau
updated_at
2. Pastikan Index Mendukung #
CREATE INDEX idx_orders_id_version ON orders(id, version);
3. Logic Berat Harus di Luar Transaction #
Transaction DB hanya untuk read final + write
4. Batasi Retry #
- Max retry (misal 3x)
- Gunakan exponential backoff
5. Pastikan Operasi Idempotent #
- Retry tidak menimbulkan efek ganda
6. Jangan Campur dengan FOR UPDATE
#
Jika perlu FOR UPDATE, akui itu pessimistic locking dan desain sesuai risikonya.
Optimistic vs Pessimistic Locking (Ringkas) #
| Aspek | Optimistic | Pessimistic |
|---|---|---|
| Lock di awal | ❌ | ✅ |
| Retry | ✅ | ❌ |
| Risiko blocking | Rendah | Tinggi |
| Cocok untuk | Low conflict | High conflict |
Penutup #
Optimistic locking bukan sekadar teknik, tapi kontrak desain:
Konflik boleh terjadi, tapi harus cepat dideteksi dan ditangani
Saat digunakan dengan benar, ia sangat scalable.
Saat digunakan asal-asalan—terutama dicampur dengan FOR UPDATE dan logic lama—ia justru menjadi silent database killer.
Desain concurrency bukan soal “aman atau tidak”, tapi soal memahami biaya dari setiap pilihan.