[{"data":1,"prerenderedAt":129},["ShallowReactive",2],{"site-header":3,"site-footer-license":37,"article-dead-letter-queue-retry-policy-rabbitmq-resilient-messaging-with-latest":41},{"navigations":4},[5,20],{"collection":6,"item":7},"series",{"__typename":6,"title":8,"slug":9,"children_series":10},"Artikel","all",[11,14,17],{"title":12,"slug":13},"Algoritma dan Struktur Data","algoritma-struktur-data",{"title":15,"slug":16},"Konsep Dasar Pemrograman","konsep-dasar-pemrograman",{"title":18,"slug":19},"Kecerdasan Artifisial","kecerdasan-artifisial",{"collection":6,"item":21},{"__typename":6,"title":22,"slug":23,"children_series":24},"Tutorial","tutorial",[25,28,31,34],{"title":26,"slug":27},"Belajar Java","belajar-java",{"title":29,"slug":30},"Belajar Pascal","belajar-pascal",{"title":32,"slug":33},"Belajar Python","belajar-python",{"title":35,"slug":36},"Belajar Golang","belajar-golang",{"name":38,"url":39,"show":40},"CC BY-SA","https://creativecommons.org/licenses/by-sa/4.0/",false,{"article":42,"renderedBody":67,"renderedTakeaway":68,"latestArticles":69,"toc":109},{"id":43,"title":44,"slug":45,"excerpt":46,"published_at":47,"thumbnail_image":48,"tags":50,"reading_time":58,"takeway":59,"body":60,"authors":61,"series":58},"43","Dead Letter Queue (DLQ) & Retry Policy: Menyelamatkan Pesan Nyasar di RabbitMQ Tanpa Bikin Worker Macet","dead-letter-queue-retry-policy-rabbitmq-resilient-messaging","Pernah nggak sih, *worker* kalian nolak satu pesan terus-terusan sampai CPU melonjak 100%? Itu namanya *poison message loop*, jebakan klasik di RabbitMQ. Yuk, kita bahas cara pasang *retry policy* dan *dead letter queue* yang bener!","2026-09-03T04:16:07.481Z",{"id":49},"6c0a7ead-4841-4073-b29e-df7ae0f2aa3c",[51,52,53,54,55,56,57],"rabbitmq","message-broker","backend","architecture","microservices","async","python",null,"- Tiga pemicu pesan masuk ke *Dead Letter Exchange* (*DLX*) di RabbitMQ: penolakan negatif (`basic.reject` / `basic.nack` dengan `requeue=false`), TTL pesan kedaluwarsa, atau antrean yang melampaui kapasitas (`x-max-length`).\n- Jebakan fatal bernama *infinite poison message loop* muncul saat *worker* terus menolak pesan dengan `requeue=true` sampai CPU nembus 100%.\n- Arsitektur *exponential backoff retry* dibangun dari kombinasi header `x-delivery-count` / metadata *retry* custom dan *delayed message exchange*.\n- Antrean *DLQ* yang terisolasi jadi *safety net* buat inspeksi manual, *audit log*, dan proses *re-play* pesan setelah bug diperbaiki.","Kalian sedang ngejalanin sistem e-commerce yang lumayan sibuk. Begitu pelanggan berhasil bayar, sistem nerbitin (*publish*) event `order.paid` ke antrean [RabbitMQ](https://www.rabbitmq.com/). Dari situ, *worker* bakal nyetak resi pengiriman, motong stok gudang, dan ngirim invoice lewat email.\n\nTiba-tiba, API penyedia logistik pihak ketiga mengalami gangguan (*down*). *Worker* pengiriman pun gagal memproses pesan itu. Nah, dari titik ini kita bakal lihat gimana jebakannya muncul.\n\nApa yang biasanya dilakukan developer pemula? Mereka nangkep *exception* (`try...except`), lalu manggil baris sakti ini:\n\n```python\n# ANTI-PATTERN: Menolak pesan dan menyuruh RabbitMQ memasukkannya kembali ke antrean\nchannel.basic_nack(delivery_tag=method.delivery_tag, requeue=True)\n```\n\nHasilnya? Pesan langsung dibalikin ke depan antrean, diambil lagi oleh *worker* dalam hitungan mikrodetik, gagal lagi, dikembalikan lagi, dan begitu terus jutaan kali per menit. 😅\n\nFenomena ini disebut **poison message loop**. Bukan cuma bikin antrean macet total (*head-of-line blocking*), CPU server *worker* kalian bakal langsung melonjak ke 100%!\n\nTerus, gimana cara nangani pesan gagal secara elegan tanpa ngeblokir antrean lain? Jawabannya ada dua senjata: **retry policy** dan *dead letter queue* (*DLQ*).\n\n## Apa Sih Sebenarnya *Dead Letter Exchange* Itu?\n\nDi [RabbitMQ](https://www.rabbitmq.com/), *dead lettering* bukanlah fitur magis yang rumit. *Dead Letter Exchange* (*DLX*) sebenarnya cuma *exchange* AMQP biasa yang kita daftarkan ke sebuah antrean lewat argumen konfigurasi `x-dead-letter-exchange`.\n\nSebuah pesan dianggap \"mati\" (*dead-lettered*) dan diteruskan otomatis oleh *broker* RabbitMQ ke *DLX* hanya kalau terjadi salah satu dari tiga kondisi ini.\n\nPertama, pesan ditolak eksplisit oleh *worker* pakai `basic.reject` atau `basic.nack` dengan parameter `requeue=False`.\n\nKedua, masa berlaku pesan habis (*TTL expired*). Pesan punya batas waktu hidup (*Time-To-Live*), baik di level antrean (`x-message-ttl`) atau per pesan individual, dan waktunya kedaluwarsa sebelum sempat diproses.\n\nKetiga, antrean melebihi batas (*max length reached*). Antrean utama udah nyampe batas maksimum pesan (`x-max-length`), jadi pesan paling awal dibuang ke *DLX*.\n\n## Kenapa Butuh *Retry* Bertingkat Sih?\n\nKetika sebuah pesan gagal diproses, penyebabnya biasanya terbagi dua kategori.\n\nAda *transient failure*, alias kesalahan sementara: jaringan *glitch* sesaat, *database* lagi *lock contention*, atau API pihak ketiga lambat merespons. Solusinya, coba lagi beberapa detik kemudian (*retry with backoff*).\n\nAda juga *permanent failure*, alias kesalahan permanen: payload JSON rusak, ID pengguna nggak valid, atau bug logika di kode. Berapa kali pun dicoba ulang, pesan ini nggak akan pernah berhasil!\n\nStrategi idealnya, kasih kesempatan *retry* otomatis beberapa kali dengan jeda yang makin lama (misal 5 detik, 15 detik, 60 detik). Kalau setelah 3 atau 5 percobaan masih tetap gagal, barulah pesannya dilempar ke *Dead Letter Queue* (*DLQ*) akhir buat dianalisis developer.\n\n## Pola Andalan: *Delayed Retry* via TTL dan DLX\n\nSalah satu pola paling andal di RabbitMQ tanpa *plugin* eksternal adalah memanfaatkan antrean perantara (*retry queue*) yang dipasangi TTL.\n\nPertama, *worker* mengonsumsi pesan dari *main queue* (`orders.process`). Kalau gagal sementara, *worker* mem-*publish* pesan ke `retry.exchange` sambil nyatet counter percobaan di header `x-retries: 1`.\n\nKedua, pesan mendarat di *retry queue* (`orders.retry.30s`). Antrean ini nggak punya *worker* sama sekali! Dia cuma dipasangi `x-message-ttl: 30000` (30 detik) dan `x-dead-letter-exchange: orders.exchange`.\n\nKetiga, setelah 30 detik tidur di *retry queue*, RabbitMQ otomatis menganggap pesan itu \"expired\" dan meneruskannya kembali ke `orders.process` buat dicoba ulang oleh *worker*.\n\nKeempat, kalau header `x-retries` udah nyampe batas maksimum (misal 3), *worker* langsung ngirim pesan ke `orders.dlq` (*terminal queue*). Selesai, nggak ada drama berulang. 😎\n\n## Contoh Implementasi di Python (Pika / Aio-Pika)\n\nBerikut contoh deklarasi antrean tangguh pakai Python dan pustaka `pika`:\n\n```python\nimport json\nimport pika\n\nconnection = pika.BlockingConnection(pika.ConnectionParameters(host=\"localhost\"))\nchannel = connection.channel()\n\n# 1. Buat Dead Letter Exchange (DLX) dan Antrean DLQ\nchannel.exchange_declare(exchange=\"dlx.orders\", exchange_type=\"direct\")\nchannel.queue_declare(queue=\"orders.dlq\", durable=True)\nchannel.queue_bind(exchange=\"dlx.orders\", queue=\"orders.dlq\", routing_key=\"order.failed\")\n\n# 2. Buat Main Exchange & Queue dengan konfigurasi DLX bawaan\nchannel.exchange_declare(exchange=\"orders.exchange\", exchange_type=\"direct\")\n\nqueue_args = {\n    \"x-dead-letter-exchange\": \"dlx.orders\",\n    \"x-dead-letter-routing-key\": \"order.failed\",\n}\n\nchannel.queue_declare(queue=\"orders.main\", durable=True, arguments=queue_args)\nchannel.queue_bind(exchange=\"orders.exchange\", queue=\"orders.main\", routing_key=\"order.process\")\n\n# 3. Callback Worker yang Aman\ndef on_message(ch, method, properties, body):\n    try:\n        data = json.loads(body)\n        # Eksekusi logika bisnis (misal call API logistik)...\n        process_shipment(data)\n        ch.basic_ack(delivery_tag=method.delivery_tag)\n    except TransientNetworkError:\n        # Kesalahan sementara: bisa dipublish ke retry queue\n        print(\"Network timeout, routing to retry...\")\n        ch.basic_nack(delivery_tag=method.delivery_tag, requeue=False)  # Masuk ke DLQ jika batas habis\n    except Exception as e:\n        # Kesalahan permanen: buang langsung ke DLQ, JANGAN REQUEUE!\n        print(f\"Permanent error: {e}. Moving to DLQ.\")\n        ch.basic_nack(delivery_tag=method.delivery_tag, requeue=False)\n\nchannel.basic_consume(queue=\"orders.main\", on_message_callback=on_message)\nprint(\"Worker siap melayani pesanan...\")\nchannel.start_consuming()\n```\n\n## Terus, Pesan yang Nyangkut di DLQ Diapain?\n\nAntrean *DLQ* bukan tempat sampah tempat data dilupakan gitu aja. *DLQ* itu brankas penyelamat, dan ada tiga hal yang wajib kalian lakukan.\n\nPasang *alerting* terintegrasi. Setel metrik *monitoring* (misal via Prometheus RabbitMQ Exporter) buat bunyiin alarm begitu jumlah pesan di `orders.dlq` lebih besar dari 0.\n\nLakukan inspeksi dan *root cause analysis*. Developer bisa lihat payload asli dan header error di RabbitMQ Management UI buat mendiagnosis apakah ada payload aneh atau bug baru.\n\nTerakhir, jalankan *replay message*. Setelah bug di kode diperbaiki atau layanan pihak ketiga kembali online, kalian bisa pakai CLI `rabbitmqadmin` atau *script* sederhana buat mindahin (*shovel*) pesan dari *DLQ* balik ke antrean utama. Nggak ada transaksi pelanggan yang hilang!\n\n## Jangan Biarkan Satu Pesan Gagal Nurunin Sistemmu\n\nSistem terdistribusi yang tangguh (*resilient*) bukan sistem yang nggak pernah gagal, melainkan sistem yang tahu cara memperlakukan kegagalan dengan anggun.\n\nDengan mengganti `requeue=True` yang ceroboh jadi kombinasi *retry backoff* dan *dead letter queue*, kalian melindungi *worker* backend dari kelumpuhan. Setiap pesan penting tetap tersimpan dengan selamat, siap diproses ulang kapan pun bug-nya kelar dibetulin.\n\nSelamat ber-*messaging* ria, dan semoga nggak ada lagi pesanan pelanggan yang nyasar ke *void*! 👋",[62],{"authors_id":63},{"name":64,"slug":65,"role":66,"profile_picture":58},"Inva","inva","Writer","\u003Cp>Kalian sedang ngejalanin sistem e-commerce yang lumayan sibuk. Begitu pelanggan berhasil bayar, sistem nerbitin (\u003Cem>publish\u003C/em>) event \u003Ccode>order.paid\u003C/code> ke antrean \u003Ca href=\"https://www.rabbitmq.com/\">RabbitMQ\u003C/a>. Dari situ, \u003Cem>worker\u003C/em> bakal nyetak resi pengiriman, motong stok gudang, dan ngirim invoice lewat email.\u003C/p>\n\u003Cp>Tiba-tiba, API penyedia logistik pihak ketiga mengalami gangguan (\u003Cem>down\u003C/em>). \u003Cem>Worker\u003C/em> pengiriman pun gagal memproses pesan itu. Nah, dari titik ini kita bakal lihat gimana jebakannya muncul.\u003C/p>\n\u003Cp>Apa yang biasanya dilakukan developer pemula? Mereka nangkep \u003Cem>exception\u003C/em> (\u003Ccode>try...except\u003C/code>), lalu manggil baris sakti ini:\u003C/p>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black/40 inline-block\">\u003C/span>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black/40 inline-block\">\u003C/span>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black/40 inline-block\">\u003C/span>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">PYTHON\u003C/span>\n    \u003C/div>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%23%20ANTI-PATTERN%3A%20Menolak%20pesan%20dan%20menyuruh%20RabbitMQ%20memasukkannya%20kembali%20ke%20antrean%0Achannel.basic_nack(delivery_tag%3Dmethod.delivery_tag%2C%20requeue%3DTrue)\" title=\"Salin kode\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C/path>\n      \u003C/svg>\n      \u003Cspan class=\"copy-text\">Copy\u003C/span>\n    \u003C/button>\n  \u003C/div>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># ANTI-PATTERN: Menolak pesan dan menyuruh RabbitMQ memasukkannya kembali ke antrean\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">channel.basic_nack(\u003C/span>\u003Cspan style=\"color:#FFAB70\">delivery_tag\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\">method.delivery_tag, \u003C/span>\u003Cspan style=\"color:#FFAB70\">requeue\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#79B8FF\">True\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n  \u003C/div>\n\u003C/div>\u003Cp>Hasilnya? Pesan langsung dibalikin ke depan antrean, diambil lagi oleh \u003Cem>worker\u003C/em> dalam hitungan mikrodetik, gagal lagi, dikembalikan lagi, dan begitu terus jutaan kali per menit. 😅\u003C/p>\n\u003Cp>Fenomena ini disebut \u003Cstrong>poison message loop\u003C/strong>. Bukan cuma bikin antrean macet total (\u003Cem>head-of-line blocking\u003C/em>), CPU server \u003Cem>worker\u003C/em> kalian bakal langsung melonjak ke 100%!\u003C/p>\n\u003Cp>Terus, gimana cara nangani pesan gagal secara elegan tanpa ngeblokir antrean lain? Jawabannya ada dua senjata: \u003Cstrong>retry policy\u003C/strong> dan \u003Cem>dead letter queue\u003C/em> (\u003Cem>DLQ\u003C/em>).\u003C/p>\n\u003Ch2 id=\"apa-sih-sebenarnya-dead-letter-exchange-itu\">Apa Sih Sebenarnya \u003Cem>Dead Letter Exchange\u003C/em> Itu?\u003C/h2>\n\u003Cp>Di \u003Ca href=\"https://www.rabbitmq.com/\">RabbitMQ\u003C/a>, \u003Cem>dead lettering\u003C/em> bukanlah fitur magis yang rumit. \u003Cem>Dead Letter Exchange\u003C/em> (\u003Cem>DLX\u003C/em>) sebenarnya cuma \u003Cem>exchange\u003C/em> AMQP biasa yang kita daftarkan ke sebuah antrean lewat argumen konfigurasi \u003Ccode>x-dead-letter-exchange\u003C/code>.\u003C/p>\n\u003Cp>Sebuah pesan dianggap &quot;mati&quot; (\u003Cem>dead-lettered\u003C/em>) dan diteruskan otomatis oleh \u003Cem>broker\u003C/em> RabbitMQ ke \u003Cem>DLX\u003C/em> hanya kalau terjadi salah satu dari tiga kondisi ini.\u003C/p>\n\u003Cp>Pertama, pesan ditolak eksplisit oleh \u003Cem>worker\u003C/em> pakai \u003Ccode>basic.reject\u003C/code> atau \u003Ccode>basic.nack\u003C/code> dengan parameter \u003Ccode>requeue=False\u003C/code>.\u003C/p>\n\u003Cp>Kedua, masa berlaku pesan habis (\u003Cem>TTL expired\u003C/em>). Pesan punya batas waktu hidup (\u003Cem>Time-To-Live\u003C/em>), baik di level antrean (\u003Ccode>x-message-ttl\u003C/code>) atau per pesan individual, dan waktunya kedaluwarsa sebelum sempat diproses.\u003C/p>\n\u003Cp>Ketiga, antrean melebihi batas (\u003Cem>max length reached\u003C/em>). Antrean utama udah nyampe batas maksimum pesan (\u003Ccode>x-max-length\u003C/code>), jadi pesan paling awal dibuang ke \u003Cem>DLX\u003C/em>.\u003C/p>\n\u003Ch2 id=\"kenapa-butuh-retry-bertingkat-sih\">Kenapa Butuh \u003Cem>Retry\u003C/em> Bertingkat Sih?\u003C/h2>\n\u003Cp>Ketika sebuah pesan gagal diproses, penyebabnya biasanya terbagi dua kategori.\u003C/p>\n\u003Cp>Ada \u003Cem>transient failure\u003C/em>, alias kesalahan sementara: jaringan \u003Cem>glitch\u003C/em> sesaat, \u003Cem>database\u003C/em> lagi \u003Cem>lock contention\u003C/em>, atau API pihak ketiga lambat merespons. Solusinya, coba lagi beberapa detik kemudian (\u003Cem>retry with backoff\u003C/em>).\u003C/p>\n\u003Cp>Ada juga \u003Cem>permanent failure\u003C/em>, alias kesalahan permanen: payload JSON rusak, ID pengguna nggak valid, atau bug logika di kode. Berapa kali pun dicoba ulang, pesan ini nggak akan pernah berhasil!\u003C/p>\n\u003Cp>Strategi idealnya, kasih kesempatan \u003Cem>retry\u003C/em> otomatis beberapa kali dengan jeda yang makin lama (misal 5 detik, 15 detik, 60 detik). Kalau setelah 3 atau 5 percobaan masih tetap gagal, barulah pesannya dilempar ke \u003Cem>Dead Letter Queue\u003C/em> (\u003Cem>DLQ\u003C/em>) akhir buat dianalisis developer.\u003C/p>\n\u003Ch2 id=\"pola-andalan-delayed-retry-via-ttl-dan-dlx\">Pola Andalan: \u003Cem>Delayed Retry\u003C/em> via TTL dan DLX\u003C/h2>\n\u003Cp>Salah satu pola paling andal di RabbitMQ tanpa \u003Cem>plugin\u003C/em> eksternal adalah memanfaatkan antrean perantara (\u003Cem>retry queue\u003C/em>) yang dipasangi TTL.\u003C/p>\n\u003Cp>Pertama, \u003Cem>worker\u003C/em> mengonsumsi pesan dari \u003Cem>main queue\u003C/em> (\u003Ccode>orders.process\u003C/code>). Kalau gagal sementara, \u003Cem>worker\u003C/em> mem-\u003Cem>publish\u003C/em> pesan ke \u003Ccode>retry.exchange\u003C/code> sambil nyatet counter percobaan di header \u003Ccode>x-retries: 1\u003C/code>.\u003C/p>\n\u003Cp>Kedua, pesan mendarat di \u003Cem>retry queue\u003C/em> (\u003Ccode>orders.retry.30s\u003C/code>). Antrean ini nggak punya \u003Cem>worker\u003C/em> sama sekali! Dia cuma dipasangi \u003Ccode>x-message-ttl: 30000\u003C/code> (30 detik) dan \u003Ccode>x-dead-letter-exchange: orders.exchange\u003C/code>.\u003C/p>\n\u003Cp>Ketiga, setelah 30 detik tidur di \u003Cem>retry queue\u003C/em>, RabbitMQ otomatis menganggap pesan itu &quot;expired&quot; dan meneruskannya kembali ke \u003Ccode>orders.process\u003C/code> buat dicoba ulang oleh \u003Cem>worker\u003C/em>.\u003C/p>\n\u003Cp>Keempat, kalau header \u003Ccode>x-retries\u003C/code> udah nyampe batas maksimum (misal 3), \u003Cem>worker\u003C/em> langsung ngirim pesan ke \u003Ccode>orders.dlq\u003C/code> (\u003Cem>terminal queue\u003C/em>). Selesai, nggak ada drama berulang. 😎\u003C/p>\n\u003Ch2 id=\"contoh-implementasi-di-python-pika-aio-pika\">Contoh Implementasi di Python (Pika / Aio-Pika)\u003C/h2>\n\u003Cp>Berikut contoh deklarasi antrean tangguh pakai Python dan pustaka \u003Ccode>pika\u003C/code>:\u003C/p>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black/40 inline-block\">\u003C/span>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black/40 inline-block\">\u003C/span>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black/40 inline-block\">\u003C/span>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">PYTHON\u003C/span>\n    \u003C/div>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"import%20json%0Aimport%20pika%0A%0Aconnection%20%3D%20pika.BlockingConnection(pika.ConnectionParameters(host%3D%22localhost%22))%0Achannel%20%3D%20connection.channel()%0A%0A%23%201.%20Buat%20Dead%20Letter%20Exchange%20(DLX)%20dan%20Antrean%20DLQ%0Achannel.exchange_declare(exchange%3D%22dlx.orders%22%2C%20exchange_type%3D%22direct%22)%0Achannel.queue_declare(queue%3D%22orders.dlq%22%2C%20durable%3DTrue)%0Achannel.queue_bind(exchange%3D%22dlx.orders%22%2C%20queue%3D%22orders.dlq%22%2C%20routing_key%3D%22order.failed%22)%0A%0A%23%202.%20Buat%20Main%20Exchange%20%26%20Queue%20dengan%20konfigurasi%20DLX%20bawaan%0Achannel.exchange_declare(exchange%3D%22orders.exchange%22%2C%20exchange_type%3D%22direct%22)%0A%0Aqueue_args%20%3D%20%7B%0A%20%20%20%20%22x-dead-letter-exchange%22%3A%20%22dlx.orders%22%2C%0A%20%20%20%20%22x-dead-letter-routing-key%22%3A%20%22order.failed%22%2C%0A%7D%0A%0Achannel.queue_declare(queue%3D%22orders.main%22%2C%20durable%3DTrue%2C%20arguments%3Dqueue_args)%0Achannel.queue_bind(exchange%3D%22orders.exchange%22%2C%20queue%3D%22orders.main%22%2C%20routing_key%3D%22order.process%22)%0A%0A%23%203.%20Callback%20Worker%20yang%20Aman%0Adef%20on_message(ch%2C%20method%2C%20properties%2C%20body)%3A%0A%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20data%20%3D%20json.loads(body)%0A%20%20%20%20%20%20%20%20%23%20Eksekusi%20logika%20bisnis%20(misal%20call%20API%20logistik)...%0A%20%20%20%20%20%20%20%20process_shipment(data)%0A%20%20%20%20%20%20%20%20ch.basic_ack(delivery_tag%3Dmethod.delivery_tag)%0A%20%20%20%20except%20TransientNetworkError%3A%0A%20%20%20%20%20%20%20%20%23%20Kesalahan%20sementara%3A%20bisa%20dipublish%20ke%20retry%20queue%0A%20%20%20%20%20%20%20%20print(%22Network%20timeout%2C%20routing%20to%20retry...%22)%0A%20%20%20%20%20%20%20%20ch.basic_nack(delivery_tag%3Dmethod.delivery_tag%2C%20requeue%3DFalse)%20%20%23%20Masuk%20ke%20DLQ%20jika%20batas%20habis%0A%20%20%20%20except%20Exception%20as%20e%3A%0A%20%20%20%20%20%20%20%20%23%20Kesalahan%20permanen%3A%20buang%20langsung%20ke%20DLQ%2C%20JANGAN%20REQUEUE!%0A%20%20%20%20%20%20%20%20print(f%22Permanent%20error%3A%20%7Be%7D.%20Moving%20to%20DLQ.%22)%0A%20%20%20%20%20%20%20%20ch.basic_nack(delivery_tag%3Dmethod.delivery_tag%2C%20requeue%3DFalse)%0A%0Achannel.basic_consume(queue%3D%22orders.main%22%2C%20on_message_callback%3Don_message)%0Aprint(%22Worker%20siap%20melayani%20pesanan...%22)%0Achannel.start_consuming()\" title=\"Salin kode\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C/path>\n      \u003C/svg>\n      \u003Cspan class=\"copy-text\">Copy\u003C/span>\n    \u003C/button>\n  \u003C/div>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> json\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> pika\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">connection \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> pika.BlockingConnection(pika.ConnectionParameters(\u003C/span>\u003Cspan style=\"color:#FFAB70\">host\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"localhost\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">))\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">channel \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> connection.channel()\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># 1. Buat Dead Letter Exchange (DLX) dan Antrean DLQ\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">channel.exchange_declare(\u003C/span>\u003Cspan style=\"color:#FFAB70\">exchange\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"dlx.orders\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#FFAB70\">exchange_type\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"direct\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">channel.queue_declare(\u003C/span>\u003Cspan style=\"color:#FFAB70\">queue\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"orders.dlq\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#FFAB70\">durable\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#79B8FF\">True\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">channel.queue_bind(\u003C/span>\u003Cspan style=\"color:#FFAB70\">exchange\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"dlx.orders\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#FFAB70\">queue\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"orders.dlq\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#FFAB70\">routing_key\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"order.failed\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># 2. Buat Main Exchange &#x26; Queue dengan konfigurasi DLX bawaan\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">channel.exchange_declare(\u003C/span>\u003Cspan style=\"color:#FFAB70\">exchange\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"orders.exchange\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#FFAB70\">exchange_type\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"direct\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">queue_args \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    \"x-dead-letter-exchange\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"dlx.orders\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    \"x-dead-letter-routing-key\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"order.failed\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">}\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">channel.queue_declare(\u003C/span>\u003Cspan style=\"color:#FFAB70\">queue\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"orders.main\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#FFAB70\">durable\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#79B8FF\">True\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#FFAB70\">arguments\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\">queue_args)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">channel.queue_bind(\u003C/span>\u003Cspan style=\"color:#FFAB70\">exchange\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"orders.exchange\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#FFAB70\">queue\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"orders.main\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#FFAB70\">routing_key\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"order.process\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># 3. Callback Worker yang Aman\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">def\u003C/span>\u003Cspan style=\"color:#B392F0\"> on_message\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(ch, method, properties, body):\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    try\u003C/span>\u003Cspan style=\"color:#E1E4E8\">:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        data \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> json.loads(body)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">        # Eksekusi logika bisnis (misal call API logistik)...\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        process_shipment(data)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        ch.basic_ack(\u003C/span>\u003Cspan style=\"color:#FFAB70\">delivery_tag\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\">method.delivery_tag)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    except\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> TransientNetworkError:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">        # Kesalahan sementara: bisa dipublish ke retry queue\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">        print\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"Network timeout, routing to retry...\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        ch.basic_nack(\u003C/span>\u003Cspan style=\"color:#FFAB70\">delivery_tag\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\">method.delivery_tag, \u003C/span>\u003Cspan style=\"color:#FFAB70\">requeue\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#79B8FF\">False\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)  \u003C/span>\u003Cspan style=\"color:#6A737D\"># Masuk ke DLQ jika batas habis\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    except\u003C/span>\u003Cspan style=\"color:#79B8FF\"> Exception\u003C/span>\u003Cspan style=\"color:#F97583\"> as\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> e:\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">        # Kesalahan permanen: buang langsung ke DLQ, JANGAN REQUEUE!\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">        print\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#F97583\">f\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"Permanent error: \u003C/span>\u003Cspan style=\"color:#79B8FF\">{\u003C/span>\u003Cspan style=\"color:#E1E4E8\">e\u003C/span>\u003Cspan style=\"color:#79B8FF\">}\u003C/span>\u003Cspan style=\"color:#9ECBFF\">. Moving to DLQ.\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        ch.basic_nack(\u003C/span>\u003Cspan style=\"color:#FFAB70\">delivery_tag\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\">method.delivery_tag, \u003C/span>\u003Cspan style=\"color:#FFAB70\">requeue\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#79B8FF\">False\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">channel.basic_consume(\u003C/span>\u003Cspan style=\"color:#FFAB70\">queue\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"orders.main\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#FFAB70\">on_message_callback\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\">on_message)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">print\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"Worker siap melayani pesanan...\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">channel.start_consuming()\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n  \u003C/div>\n\u003C/div>\u003Ch2 id=\"terus-pesan-yang-nyangkut-di-dlq-diapain\">Terus, Pesan yang Nyangkut di DLQ Diapain?\u003C/h2>\n\u003Cp>Antrean \u003Cem>DLQ\u003C/em> bukan tempat sampah tempat data dilupakan gitu aja. \u003Cem>DLQ\u003C/em> itu brankas penyelamat, dan ada tiga hal yang wajib kalian lakukan.\u003C/p>\n\u003Cp>Pasang \u003Cem>alerting\u003C/em> terintegrasi. Setel metrik \u003Cem>monitoring\u003C/em> (misal via Prometheus RabbitMQ Exporter) buat bunyiin alarm begitu jumlah pesan di \u003Ccode>orders.dlq\u003C/code> lebih besar dari 0.\u003C/p>\n\u003Cp>Lakukan inspeksi dan \u003Cem>root cause analysis\u003C/em>. Developer bisa lihat payload asli dan header error di RabbitMQ Management UI buat mendiagnosis apakah ada payload aneh atau bug baru.\u003C/p>\n\u003Cp>Terakhir, jalankan \u003Cem>replay message\u003C/em>. Setelah bug di kode diperbaiki atau layanan pihak ketiga kembali online, kalian bisa pakai CLI \u003Ccode>rabbitmqadmin\u003C/code> atau \u003Cem>script\u003C/em> sederhana buat mindahin (\u003Cem>shovel\u003C/em>) pesan dari \u003Cem>DLQ\u003C/em> balik ke antrean utama. Nggak ada transaksi pelanggan yang hilang!\u003C/p>\n\u003Ch2 id=\"jangan-biarkan-satu-pesan-gagal-nurunin-sistemmu\">Jangan Biarkan Satu Pesan Gagal Nurunin Sistemmu\u003C/h2>\n\u003Cp>Sistem terdistribusi yang tangguh (\u003Cem>resilient\u003C/em>) bukan sistem yang nggak pernah gagal, melainkan sistem yang tahu cara memperlakukan kegagalan dengan anggun.\u003C/p>\n\u003Cp>Dengan mengganti \u003Ccode>requeue=True\u003C/code> yang ceroboh jadi kombinasi \u003Cem>retry backoff\u003C/em> dan \u003Cem>dead letter queue\u003C/em>, kalian melindungi \u003Cem>worker\u003C/em> backend dari kelumpuhan. Setiap pesan penting tetap tersimpan dengan selamat, siap diproses ulang kapan pun bug-nya kelar dibetulin.\u003C/p>\n\u003Cp>Selamat ber-\u003Cem>messaging\u003C/em> ria, dan semoga nggak ada lagi pesanan pelanggan yang nyasar ke \u003Cem>void\u003C/em>! 👋\u003C/p>\n","\u003Cul>\n\u003Cli>Tiga pemicu pesan masuk ke \u003Cem>Dead Letter Exchange\u003C/em> (\u003Cem>DLX\u003C/em>) di RabbitMQ: penolakan negatif (\u003Ccode>basic.reject\u003C/code> / \u003Ccode>basic.nack\u003C/code> dengan \u003Ccode>requeue=false\u003C/code>), TTL pesan kedaluwarsa, atau antrean yang melampaui kapasitas (\u003Ccode>x-max-length\u003C/code>).\u003C/li>\n\u003Cli>Jebakan fatal bernama \u003Cem>infinite poison message loop\u003C/em> muncul saat \u003Cem>worker\u003C/em> terus menolak pesan dengan \u003Ccode>requeue=true\u003C/code> sampai CPU nembus 100%.\u003C/li>\n\u003Cli>Arsitektur \u003Cem>exponential backoff retry\u003C/em> dibangun dari kombinasi header \u003Ccode>x-delivery-count\u003C/code> / metadata \u003Cem>retry\u003C/em> custom dan \u003Cem>delayed message exchange\u003C/em>.\u003C/li>\n\u003Cli>Antrean \u003Cem>DLQ\u003C/em> yang terisolasi jadi \u003Cem>safety net\u003C/em> buat inspeksi manual, \u003Cem>audit log\u003C/em>, dan proses \u003Cem>re-play\u003C/em> pesan setelah bug diperbaiki.\u003C/li>\n\u003C/ul>\n",[70,83,96,106],{"id":71,"title":72,"slug":73,"excerpt":74,"thumbnail_image":75,"tags":77},"46","Mengapa C Mulai Ditinggalkan: Duel Filosofi Keamanan Rust vs Kesederhanaan Zig","mengapa-c-mulai-ditinggalkan-duel-filosofi-rust-vs-zig","Pernah denger kalau sekitar 70% celah keamanan kritis software modern itu asalnya dari bahasa C? Iya, bahasa yang udah jadi fondasi internet ini 😅. Yuk, kita bedah duel filosofi Rust vs Zig dan cari tahu mana yang pas buat proyek kalian!",{"id":76},"1942a5c9-c1f6-476d-9913-768f78d9d8eb",[78,79,80,81,82],"system-programming","rust","zig","c","memory-safety",{"id":84,"title":85,"slug":86,"excerpt":87,"thumbnail_image":88,"tags":90},"45","Rate Limiting Algorithms: Token Bucket vs Leaky Bucket vs Sliding Window","rate-limiting-algorithms-token-bucket-leaky-bucket-sliding-window","Udah tahu kenapa *rate limiting* itu wajib, tapi masih bingung beda Token Bucket, Leaky Bucket, Fixed Window, dan Sliding Window? Yuk, kita adu empat algoritma ini bareng-bareng dan cari tahu mana yang paling pas buat API kalian!",{"id":89},"0e0ed26d-20c6-443c-bde1-4a6200f6f801",[53,91,92,93,94,54,95],"system-design","rate-limiting","redis","api-gateway","traffict-management",{"id":97,"title":98,"slug":99,"excerpt":100,"thumbnail_image":101,"tags":103},"44","Transactional Outbox Pattern: Solusi Konsistensi Database dan Message Broker Tanpa 2PC","transactional-outbox-pattern-konsistensi-database-message-broker","Pernah nggak kalian lihat pesanan udah masuk database, tapi email konfirmasinya nggak pernah kekirim? Itu gejala *dual-write problem*. Yuk, kita bedah Transactional Outbox Pattern biar data dan *event* kalian nggak lagi berselisih paham!",{"id":102},"e3c617fd-1486-4b82-b937-94fbec846c77",[53,55,54,104,52,91,105],"database","postgresql",{"id":43,"title":44,"slug":45,"excerpt":46,"thumbnail_image":107,"tags":108},{"id":49},[51,52,53,54,55,56,57],[110,114,117,120,123,126],{"text":111,"depth":112,"id":113},"Apa Sih Sebenarnya Dead Letter Exchange Itu?",2,"apa-sih-sebenarnya-dead-letter-exchange-itu",{"text":115,"depth":112,"id":116},"Kenapa Butuh Retry Bertingkat Sih?","kenapa-butuh-retry-bertingkat-sih",{"text":118,"depth":112,"id":119},"Pola Andalan: Delayed Retry via TTL dan DLX","pola-andalan-delayed-retry-via-ttl-dan-dlx",{"text":121,"depth":112,"id":122},"Contoh Implementasi di Python (Pika / Aio-Pika)","contoh-implementasi-di-python-pika-aio-pika",{"text":124,"depth":112,"id":125},"Terus, Pesan yang Nyangkut di DLQ Diapain?","terus-pesan-yang-nyangkut-di-dlq-diapain",{"text":127,"depth":112,"id":128},"Jangan Biarkan Satu Pesan Gagal Nurunin Sistemmu","jangan-biarkan-satu-pesan-gagal-nurunin-sistemmu",1791045071520]