Seribu orang ngeklik tombol "Beli Sekarang" di detik yang sama, cuma buat rebutan 1 tiket konser terakhir. Kira-kira, apa yang terjadi di server? 🤔
Tanpa mekanisme pengamanan concurrency yang bener, sistem kalian bisa kena bencana yang namanya Race Condition atau Lost Update Problem. Tiketnya kejual ke 5 orang sekaligus, sementara stok di database malah berubah minus. Kacau banget, kan? 😅
Di dunia backend dan database, dua transaksi yang nge-update row yang sama secara bersamaan harus diatur dengan cermat. Dua pendekatan yang paling sering dipakai buat nanganin rebutan data ini adalah Pessimistic Locking dan Optimistic Locking.
Yuk, kita bedah bedanya, plus kapan mending pakai yang mana.
Poin Penting
- Race Condition dan Lost Update Problem muncul pas banyak pengguna ngedit row yang sama secara barengan.
- Pessimistic Locking ngunci data secara fisik di level database lewat
SELECT ... FOR UPDATE, cocok buat persaingan tinggi kayak flash sale tiket. - Optimistic Locking main di kolom version atau timestamp tanpa ngunci row, pas buat trafik high read dengan frekuensi konflik rendah.
- Pemilihan strategi yang tepat ngejaga integritas data tanpa ngorbanin throughput aplikasi.
Gimana Cara Orang Ngedit Dokumen Bareng?
Biar kebayang, anggap aja dua transaksi itu kayak dua orang yang ngedit dokumen yang sama di kantor.
Gaya pertama itu pessimistic: seorang staf ngambil dokumen fisik dari lemari arsip, terus ngunci lemarinya pakai gembok dan bawa kuncinya. Siapa pun yang mau baca atau ngedit harus ngantre berdiri di depan lemari sampai staf itu kelar. Filosofinya kira-kira: "Ah, paling bakal ada yang ngubah barengan, mending dikunci dari awal aja, deh."
Gaya kedua itu optimistic: dokumennya difotokopi dan dibagi ke semua orang, tiap lembar dikasih nomor versi. Semua bebas ngedit di mejanya masing-masing. Pas mau nyimpen ke arsip pusat, barulah dicek—"masih versi 1.0 nggak? Kalau iya, simpan dan naikin jadi 1.1. Kalau udah ada yang nyimpen 1.1 lebih dulu, revisimu ditolak, ambil salinan terbaru!" Gaya ini santai di awal, tapi tegas di akhir.
Apa Sih Pessimistic Locking Itu?
Pessimistic Locking bekerja dengan ngunci row (row-level lock) atau tabel langsung di database. Fitur ini disediakan lewat transaksi relasional di database kayak PostgreSQL atau MySQL.
Pas transaksi dimulai, query-nya baca data pakai klausa SELECT ... FOR UPDATE:
BEGIN;
-- Mengunci baris produk ID 42 agar nggak bisa diubah transaksi lain
SELECT id, stock, price
FROM products
WHERE id = 42
FOR UPDATE;
-- Lakukan kalkulasi di aplikasi
-- Update stok jika masih tersedia
UPDATE products
SET stock = stock - 1
WHERE id = 42;
COMMIT; -- Gembok dilepas, transaksi lain baru boleh masuk
Kelebihannya jelas: aman. Kita dijamiin 100% nggak ada data yang tumpang tindih, soalnya transaksi lain dipaksa ngantre (blocking).
Tapi, ada harganya. Throughput bisa turun drastis kalau banyak request ngantre di row yang sama. Dan kalau kita kurang hati-hati ngatur urutan penguncian, sistem bisa kena Deadlock (kebuntuan) di mana dua transaksi saling nunggu selamanya.
Terus, Optimistic Locking Kerjanya Gimana?
Beda sama yang tadi, Optimistic Locking sebenernya nggak ngunci apa pun secara fisik. Pendekatan ini ngandelin logika aplikasi, dengan bantuan kolom penanda versi kayak version INT atau updated_at TIMESTAMP.
Alurnya kira-kira gini:
- Baca data beserta versinya sekarang.
SQL
SELECT id, stock, version FROM products WHERE id = 42; -- Hasil: stock = 10, version = 1 - Hitung di aplikasi, terus simpan cuma kalau versinya belum berubah.
SQL
UPDATE products SET stock = stock - 1, version = version + 1 WHERE id = 42 AND version = 1; - Cek jumlah row yang kena dampak (rows affected). Kalau hasilnya 1, berarti berhasil dan versinya naik jadi 2. Kalau hasilnya 0, artinya ada transaksi lain yang nyimpen lebih dulu—aplikasi bisa ngebatalin atau nyoba retry.
Kelebihannya, ini cepat dan nggak ada proses yang diblokir (non-blocking). Cocok banget buat aplikasi yang beban bacanya tinggi (high read throughput).
Kekurangannya, kalau tingkat bentrokan (contention) tinggi—misal rebutan 1 tiket konser—mayoritas transaksi bakal gagal. Retry beruntun itulah yang akhirnya ngabisin CPU kalian. 😅
Contoh Kode di Python / SQLAlchemy
Berikut perbandingannya pakai SQLAlchemy Concurrency:
from sqlalchemy.orm import Session
from sqlalchemy import select
from models import Product
# 1. Implementasi Pessimistic Locking
def buy_product_pessimistic(db: Session, product_id: int, quantity: int):
# Menggunakan with_for_update() untuk memicu 'SELECT ... FOR UPDATE'
product = db.execute(
select(Product)
.where(Product.id == product_id)
.with_for_update()
).scalar_one()
if product.stock < quantity:
raise ValueError("Stok nggak mencukupi!")
product.stock -= quantity
db.commit()
return product
# 2. Implementasi Optimistic Locking
def buy_product_optimistic(db: Session, product_id: int, quantity: int):
# Baca data tanpa lock fisik
product = db.execute(
select(Product).where(Product.id == product_id)
).scalar_one()
if product.stock < quantity:
raise ValueError("Stok nggak mencukupi!")
current_version = product.version
# Update bersyarat mencocokkan nomor versi
result = db.query(Product).filter(
Product.id == product_id,
Product.version == current_version
).update({
"stock": product.stock - quantity,
"version": current_version + 1
})
if result == 0:
db.rollback()
raise ValueError("Bentrokan data terdeteksi! Silakan ulangi transaksi.")
db.commit()
return product
Di versi optimistic, kunci utamanya ada di klausa WHERE id = 42 AND version = 1. Kalau versinya udah berubah duluan, update-nya nggak ngefek ke row mana pun, dan result bakal bernilai 0. Cek inilah yang jadi pengganti kunci fisik.
Kapan Pakai yang Mana, Sih?
Gampangnya, lihat seberapa sering bentrokan datanya terjadi.
| Kriteria | Pessimistic Locking | Optimistic Locking |
|---|---|---|
| Tingkat Bentrokan | Tinggi (banyak yang rebutan 1 row). | Rendah sampai sedang. |
| Karakteristik Trafik | Transaksi finansial, flash sale, booking. | Profil user, edit blog, katalog umum. |
| Biaya Kegagalan | Antrean waktu tunggu (blocking latency). | Biaya komputasi buat retry. |
| Mekanisme | Kunci fisik database (FOR UPDATE). |
Kolom version / timestamp di logika aplikasi. |
Jadi, buat persaingan tinggi yang butuh integritas mutlak, pakai pessimistic. Buat trafik yang didominasi baca dengan konflik jarang, optimistic jauh lebih enteng.
Jadi, Mana yang Harus Kita Pilih?
Nggak ada solusi tunggal yang sempurna buat semua kasus. Yang penting, kita paham dulu karakter transaksi di aplikasi sendiri.
Pakai Pessimistic Locking pas integritas mutlak tanpa toleransi retry bener-bener dibutuhkan di tengah persaingan tinggi. Sebaliknya, manfaatkan Optimistic Locking biar sistem tetap gesit di transaksi yang risikonya bentrok rendah.
Kalau salah pilih, bukan cuma data yang kacau—CPU kalian juga bisa ikut nangis. Hehe.
Selamat ber-locking ria, dan semoga transaksi kalian selalu selamat dari rebutan yang bikin pusing! 👋