Reliability

Deduplikasi dan Idempotensi: Serupa, tetapi Tidak Sama

Membedakan deduplikasi pesan dan idempotensi efek agar retry, replay, serta pemrosesan ulang tidak menghasilkan perubahan ganda.

Deduplikasi dan idempotensi sama-sama mengurangi efek pengulangan, tetapi bekerja pada boundary berbeda. Deduplikasi menahan pesan yang sudah terlihat, sedangkan idempotensi memastikan operasi berulang menghasilkan keadaan yang sama. Mengandalkan salah satunya saja dapat meninggalkan celah pada retry atau replay.

Inti pembahasan Deduplikasi melindungi jalur pemrosesan; idempotensi melindungi efek pada tujuan. Integrasi kritis sering membutuhkan keduanya.

Perbedaannya terletak pada pertanyaan yang dijawab

Deduplikasi bertanya, “apakah saya sudah melihat identitas pesan ini dalam periode tertentu?” Jika ya, pesan dialirkan ke cabang duplikat atau dihentikan. Idempotensi bertanya, “jika operasi ini dijalankan lagi, apakah hasil akhirnya tetap sama?” Ia biasanya ditegakkan oleh API tujuan, unique constraint, ledger, atau operasi yang memang bersifat set.

AspekDeduplikasiIdempotensi
BoundarySebelum/di dalam workflowPada efek tujuan
StateKey dengan TTLKey atau aturan domain
Setelah retensiPesan dapat dianggap baruMasih aman bila key dipertahankan
ContohTolak event_id berulangReturn hasil pembayaran pertama

Kualitas perlindungan bergantung pada kunci

Kunci harus stabil pada seluruh attempt untuk kejadian yang sama. UUID yang dibuat ulang oleh workflow setiap menerima request tidak dapat mendeteksi pengulangan dari sumber. Gunakan event_id, nomor transaksi, kombinasi tenant dan external ID, atau hash canonical payload bila kontrak memang tidak menyediakan identitas.

Hash payload perlu hati-hati: perubahan urutan field atau nilai nonbisnis dapat membuat key berbeda, sementara dua kejadian sah dengan payload sama dapat dianggap duplikat. Kunci yang berasal dari identitas domain biasanya lebih dapat dijelaskan ketika audit atau rekonsiliasi dilakukan.

TTL dan retensi adalah keputusan bisnis

State deduplikasi tidak selalu disimpan selamanya. TTL perlu mencakup jendela retry dan replay yang masuk akal. TTL terlalu pendek membuka peluang duplikasi terlambat; TTL terlalu panjang menambah state dan mungkin menolak kejadian yang sah bila sumber menggunakan ulang identifier.

NitroNet menyediakan node Deduplikasi dengan state PostgreSQL dan TTL. Pada eksekusi sinkron, reservasi key dilepas ketika downstream gagal, kecuali kebijakan keep_on_failure dipilih. Pada jalur asynchronous, lifecycle delivery terpisah sehingga reservasi bertahan sampai TTL. Perbedaan ini perlu dipahami saat mendesain replay.

Menggabungkan deduplikasi dan idempotensi

  1. Validasi identitas.

    Tolak request tanpa event ID yang diwajibkan kontrak.

  2. Deduplikasi di ingress.

    Kurangi pemrosesan ulang dengan key dan TTL yang terdokumentasi.

  3. Teruskan key ke tujuan.

    Gunakan key yang sama pada header atau idempotency header.

  4. Tegakkan constraint.

    Unique key atau ledger menjadi pagar ketika state workflow tidak tersedia.

  5. Rekonsiliasi hasil.

    Hubungkan respons lama dan baru menggunakan correlation ID.

Contoh pemrosesan order

Sistem e-commerce mengirim order_event_id dan order_id. Node Deduplikasi memakai event ID agar event yang sama tidak menjalankan mapping lagi. Tulis DB menggunakan order ID dengan unique constraint. Bila insert timeout setelah commit dan delivery diulang, constraint mencegah order kedua; workflow dapat memeriksa record yang ada sebelum menentukan respons.

Event lain untuk order yang sama—misalnya status berubah—harus mempunyai event ID berbeda. Karena itu deduplikasi tidak boleh hanya memakai order ID bila beberapa kejadian sah dapat terjadi sepanjang lifecycle order.

Perhatikan concurrency ketika dua pesan dengan key sama tiba hampir bersamaan. Pemeriksaan “cek lalu simpan” yang tidak atomik memungkinkan keduanya lolos sebelum state tercatat. Reservasi perlu dilakukan sebagai satu operasi database dengan constraint atau locking yang tepat. Uji paralel harus menjadi bagian test, bukan hanya mengirim request yang sama secara berurutan.

Definisikan juga respons cabang duplikat. Sebagian konsumen cukup menerima hasil sukses sebelumnya, sebagian perlu HTTP conflict, dan proses event streaming mungkin hanya mencatat bahwa pesan diabaikan. Keputusan harus konsisten dengan kontrak sumber. Jangan menghapus bukti duplicate terlalu cepat; jumlah dan sumber pengulangan dapat mengungkap bug retry, webhook provider yang berulang, atau penyalahgunaan endpoint.

Dokumentasikan format key agar perubahan sumber tidak memecah perlindungan. Normalisasi casing, separator, tenant scope, serta versi event sebelum membuat key. Jika kontrak berganti, dukung periode transisi atau version prefix sehingga key lama tetap dapat dikenali. Jangan menggabungkan organisasi berbeda ke namespace yang sama. Pantau collision dan cabang duplicate untuk memastikan kunci tidak terlalu luas maupun terlalu sempit.

Tetapkan owner state dan prosedur ketika storage deduplikasi tidak tersedia. Fail-open meningkatkan risiko duplikat, sedangkan fail-closed menghentikan trafik; pilihan harus mengikuti dampak bisnis dan terlihat pada status workflow.

Batasan dan failure policy

Deduplikasi bukan jaminan exactly-once.

State dapat kedaluwarsa dan efek eksternal berada di boundary lain. Pastikan API tujuan idempoten, gunakan unique constraint, serta definisikan replay dan rekonsiliasi.

Uji request paralel, retry setelah timeout, replay setelah TTL, dan kegagalan downstream. Pelajari risiko at-least-once delivery serta batas retry untuk menempatkan kedua kontrol ini secara tepat.

Bawa konsep ke kasus nyata

Sedang merancang integrasi lintas-sistem?

Petakan satu alur nyata bersama tim NitroNet untuk melihat posisi agent, Core, workflow, dan batas transaksinya.

Bahas arsitektur AndaKembali ke blog
Chat WhatsApp