Idempotency #
Dalam pengembangan web application dan API modern, idempotency adalah salah satu konsep fundamental yang sering diremehkan, tetapi dampaknya sangat besar terhadap reliability, data consistency, dan user experience.
Idempotency menjadi sangat krusial ketika sistem:
- Berjalan di lingkungan terdistribusi
- Menghadapi retry otomatis (client, SDK, load balancer)
- Menggunakan asynchronous processing
- Berhadapan dengan kegagalan jaringan (network failure)
Artikel ini akan membahas secara mendalam:
- Apa itu idempotency
- Masalah nyata yang muncul tanpa idempotency
- Problem solving yang ditawarkan idempotency
- Dampaknya pada sistem
- Best practice implementasi di web application
Apa Itu Idempotency? #
Secara sederhana:
Idempotency adalah sifat sebuah operasi yang jika dijalankan satu kali atau berkali-kali, hasil akhirnya tetap sama.
Dalam konteks web application:
- Request yang sama dikirim ulang (baik sengaja atau tidak)
- Sistem tidak menghasilkan efek samping tambahan
Contoh Sederhana #
Operasi:
DELETE /users/123
Jika:
- Request dikirim 1x → user terhapus
- Request dikirim 5x → user tetap terhapus
➡️ Ini idempotent.
Sebaliknya:
POST /paymentsdengan body yang sama- Dipanggil 2x → saldo terpotong 2x
➡️ Ini tidak idempotent.
Idempotency vs HTTP Method #
Secara teori HTTP:
| Method | Idempotent | Aman Digunakan untuk Retry |
|---|---|---|
| GET | ✅ | ✅ |
| PUT | ✅ | ✅ |
| DELETE | ✅ | ✅ |
| POST | ❌ | ❌ |
| PATCH | ❌ (umumnya) | ❌ |
⚠️ Catatan penting: Walaupun GET/PUT/DELETE secara konsep idempotent, implementasi backend-lah yang menentukan apakah benar-benar idempotent atau tidak.
Masalah Nyata Tanpa Idempotency #
Duplicate Transaction #
Kasus klasik:
- Client mengirim request pembayaran
- Network timeout terjadi
- Client melakukan retry
- Server memproses request dua kali
Akibatnya:
- Saldo terpotong ganda
- Order tercatat dobel
- Masalah finansial & trust user
Inconsistent State #
Tanpa idempotency:
- Data di database bisa berada di state setengah jalan
- Side-effect (email, push notification) terkirim berkali-kali
Contoh:
- Order hanya satu
- Email konfirmasi terkirim 3x
Retry Storm #
Banyak sistem modern melakukan retry otomatis:
- Mobile SDK
- API Gateway
- Service Mesh
- Load balancer
Tanpa idempotency:
- Retry = eksekusi ulang logic bisnis
- Beban sistem meningkat drastis
Bug yang Sulit Direproduksi #
Bug idempotency biasanya:
- Tidak muncul di local
- Tidak muncul di staging
- Muncul di production saat latency tinggi
➡️ Ini tipe bug paling mahal dan sulit didiagnosa.
Idempotency sebagai Problem Solving #
Idempotency hadir sebagai solusi untuk:
- Network failure
- Timeout
- Duplicate request
- At-least-once delivery (queue, event-driven)
Dengan idempotency:
- Request boleh diulang
- Sistem tetap konsisten
- Client tidak perlu “takut retry”
Cara Kerja Idempotency (High Level) #
Client
|
|--- Request + Idempotency Key --->
|
Server
|
|--- Check idempotency key ---|
| |
| Sudah ada? ---- YES -------|--> Return cached result
| |
| NO
| |
| Execute business logic
| |
| Store result + key
| |
| Return response
Implementasi Idempotency di Web Application #
Idempotency Key #
Pendekatan paling umum:
Client mengirim header:
Idempotency-Key: uuid-v4Server:
- Menyimpan key
- Mengaitkan key dengan hasil response
Jika key yang sama datang kembali:
- Logic bisnis tidak dijalankan ulang
- Response lama dikembalikan
Penyimpanan Idempotency Key #
Umumnya disimpan di:
- Redis (paling umum)
- Database
Struktur data:
| Key | Request Hash | Response | Status | TTL |
|---|
TTL penting agar:
- Storage tidak tumbuh tanpa batas
- Key tidak digunakan selamanya
Request Hashing #
Untuk keamanan:
- Idempotency key harus konsisten dengan payload
Jika:
- Key sama
- Payload berbeda
➡️ Tolak request (400 / 409)
Ini mencegah:
- Abuse
- Bug client
Pessimistic Locking dengan Affected Rows (Atomic Claim Pattern) #
Ini adalah pola yang sering dipakai di production, terutama jika tidak ingin distributed lock terpisah (Redis lock, dsb).
Intinya:
- Gunakan database sebagai single source of truth
- Manfaatkan atomic update + affected rows untuk “mengklaim” idempotency key
Konsep dasarnya:
- Idempotency key disimpan dengan status awal (misalnya:
pending) - Request pertama mencoba mengubah status
- Hanya satu request yang berhasil mengubah status (affected rows = 1)
- Request lain akan mendapat affected rows = 0 → dianggap duplicate
Contoh Skema Tabel #
CREATE TABLE idempotency_keys (
idempotency_key VARCHAR(64) PRIMARY KEY,
request_hash VARCHAR(64),
status VARCHAR(16), -- pending | processing | completed
response JSON,
created_at TIMESTAMP
);
Alur Pessimistic Locking (Bagan Text-Based) #
Client 1 Client 2
| |
|--- Request(idempotency) --->|
| |
| |
|--- UPDATE status='processing' affected_rows=1 --->
| |
| |--- UPDATE status='processing' affected_rows=0 ---> duplicate detected
| |
|--- Execute business logic |
| |
|--- UPDATE status='completed' & store response --->
| |
|<--- Return response --------|
| |
| |<--- Return cached response / error duplicate
Kunci utamanya adalah:
- UPDATE bersifat atomic
- Database menjamin hanya satu transaksi yang sukses
Kenapa Ini Disebut Pessimistic? #
Karena:
- Kita mengasumsikan akan ada race condition
- Sejak awal, kita mengunci hak eksekusi logic bisnis
- Request lain langsung dianggap competitor
Berbeda dengan optimistic approach yang berharap conflict jarang terjadi.
Kelebihan Pendekatan Ini #
- Tidak perlu Redis / distributed lock
- Konsisten walaupun service restart
- Cocok untuk sistem finansial
- Mudah diaudit (berbasis DB)
Kekurangan & Risiko #
- Bergantung pada performa database
- Perlu cleanup key lama (TTL via cron)
- Harus hati-hati dengan transaksi lama (long-running transaction)
Best Practice Penggunaan #
Gunakan short transaction
Pisahkan:
- Claim key (DB)
- Heavy processing (di luar transaksi)
Selalu simpan response akhir
Kombinasikan dengan unique constraint jika memungkinkan
Database Constraint (Natural Idempotency) #
Alternatif atau pelengkap:
- Gunakan unique constraint
Contoh:
UNIQUE (order_id, payment_type)
Jika insert kedua terjadi:
- Database menolak
- Sistem tetap konsisten
Dampak Positif Idempotency #
Reliability Naik Drastis #
- Retry aman
- Failure tidak berdampak fatal
User Experience Lebih Baik #
- Tidak ada duplicate order
- Tidak ada double charge
Simplifikasi Client #
- Client boleh retry tanpa logic kompleks
Observability Lebih Jelas #
- Duplicate request mudah dilacak
Dampak Negatif Jika Salah Implementasi #
Idempotency yang buruk bisa menyebabkan:
- Memory leak (TTL tidak diatur)
- False positive duplicate
- Deadlock pada concurrent request
➡️ Idempotency harus dirancang, bukan sekadar ditambahkan.
Best Practice Idempotency #
Gunakan untuk Operasi dengan Side Effect #
Fokuskan idempotency pada:
- Payment
- Order creation
- Resource mutation penting
Gunakan TTL yang Masuk Akal #
Contoh:
- Payment: 24 jam
- Order creation: 1–6 jam
Locking untuk Concurrent Request #
Jika dua request dengan key sama datang bersamaan:
- Gunakan distributed lock
- Pastikan hanya satu eksekusi logic
Kembalikan Response yang Sama #
Untuk key yang sama:
- Status code
- Body
- Header
Harus konsisten.
Jangan Bergantung pada Client Saja #
- Client bisa buggy
- Server harus defensive
Kombinasikan dengan Database Constraint #
Idempotency layer ≠ pengganti constraint
Gunakan keduanya.
Kapan Idempotency Wajib? #
Wajib jika:
- Sistem finansial
- Distributed system
- Mobile application
- Event-driven architecture
Opsional (tapi tetap disarankan):
- CRUD sederhana
Penutup #
Idempotency bukan sekadar “nice to have” — ia adalah pondasi keandalan sistem modern.
Banyak bug mahal di production bukan karena logic bisnis salah, tetapi karena:
Sistem tidak siap menghadapi retry.
Dengan idempotency yang baik:
- Sistem lebih tahan banting
- Data lebih konsisten
- Tim lebih tenang saat incident
Jika Anda membangun web application serius, anggap idempotency sebagai default mindset, bukan fitur tambahan.