[{"data":1,"prerenderedAt":457},["ShallowReactive",2],{"site-header":3,"site-footer-license":42,"article-pub-sub-sendiri-lalu-belajar-idempotency-with-latest":46},{"navigations":4},[5,20,37],{"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},"Kecerdasan Artifisial","kecerdasan-artifisial",{"title":18,"slug":19},"Konsep Dasar Pemrograman","konsep-dasar-pemrograman",{"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 Golang","belajar-golang",{"title":35,"slug":36},"Belajar Python","belajar-python",{"collection":6,"item":38},{"__typename":6,"title":39,"slug":40,"children_series":41},"Lab","lab",[],{"name":43,"url":44,"show":45},"CC BY-SA","https://creativecommons.org/licenses/by-sa/4.0/",false,{"article":47,"renderedBody":408,"renderedTakeaway":409,"latestArticles":410,"toc":434},{"prerequisite":48,"id":49,"title":50,"slug":51,"excerpt":52,"published_at":48,"thumbnail_image":48,"tags":53,"reading_time":59,"takeway":60,"body":61,"authors":62,"series":68},null,"118","Belajar Idempotency dari Stok yang Keburu Kebeli","pub-sub-sendiri-lalu-belajar-idempotency","Tugas pertama saya di kerjaan pertama: sistem yang kewalahan menerima webhook. Saya putuskan membangun pub/sub sendiri dari nol, dan pelajaran yang akhirnya datang bukan dari situ — tapi dari stok gudang yang keburu kebeli orang.",[54,55,56,57,58],"idempotency","event-driven","celery","rabbitmq","post-mortem",5,"- Antrian bikin pekerjaan selesai cepat; antrian nggak bikin pekerjaan selesai benar.\n- Update yang datang tidak berurutan bikin nilai lama menang, dan stok gudang yang salah bikin barang keburu kebeli.\n- Retry itu keadaan normal, bukan tanda sistemnya rusak — jadi yang harus dipastikan bukan \"jangan dobel\", tapi \"dobel nggak mengubah apa pun\".\n- Yang menentukan siapa pemilik keputusan itu: aplikasimu, atau database.","Tugas pertama saya di kerjaan pertama bukan bikin fitur. Sistemnya sudah jalan, menerima\nnotifikasi dari luar lewat webhook, dan ditangani langsung oleh handler di aplikasi Laravel-nya.\nSelama sepi nggak ada masalah.\n\nBegitu rame, BOOM, sistemnya kewalahan dan sebagian request-nya di-drop. Request datang lebih cepat\ndaripada yang bisa dikerjakan, dan yang nggak sempat dikerjakan hilang begitu saja — nggak ada\nyang menyimpannya untuk dicoba lagi nanti.\n\n## Keputusan pertama saya, cukup mahal\n\nSaya memutuskan membangun pub/sub-nya sendiri dari nol, pakai RabbitMQ dan pika. Alasannya waktu\nitu masuk akal, dan kayaknya semua orang di posisi saya akan punya alasan yang sama: **kontrol**.\nSaya mau tahu persis apa yang terjadi di antara pesan datang dan pekerjaan selesai.\n\nYang saya dapat: pekerjaan yang tidak selesai. Antrian, reconnect, ack, retry, urutan, semuanya\nharus saya tulis sendiri — dan setiap bagian yang belum saya tulis adalah bagian yang belum jalan.\nSaya capek sendiri, dan sistemnya nggak pernah sempat saya deploy.\n\nKalau kamu sedang di posisi itu, satu hal yang saya harap kamu percaya tanpa harus kena dulu: yang\nsedang kamu bangun bukan \"sistem antrian\". Yang sedang kamu bangun adalah **seluruh daftar masalah\nyang orang lain sudah selesaikan**, dan kamu akan ketemu satu per satu, di produksi, sendirian.\n\n## Yang akhirnya dipakai\n\nSaya belajar Celery dan RabbitMQ, lalu memasangnya di depan FastAPI. Di situ semuanya jadi enak:\npekerjaan dikirim ke queue, worker mengerjakan, dan kalau gagal, ya dicoba lagi. Yang tadinya\nberminggu-minggu jadi beberapa hari, dengan kode yang jauh lebih sedikit.\n\nDan di situ juga saya salah sangka sekali lagi: saya pikir masalahnya selesai. Beberapa waktu\nmemang iya.\n\n## Gejala pertama: stok yang ketinggalan beberapa langkah\n\nLalu muncul laporan soal stok yang nggak sesuai. Bentuknya begini: masuk dua update untuk barang\nyang sama — stok 2, lalu stok 0. Hasil akhirnya harusnya 0.\n\nYang terjadi: 2 Worker mengerjakan update, yang 0 duluan, selesai, lalu update yang 2 datang dan\nmenimpa angkanya. Nggak ada satu pun dari dua update itu yang salah; yang salah cuma urutannya. Hasilnya? barangnya zero stock tapi masih bisa dibeli.\n\nIni belum bencana. Angkanya telat, dan beberapa saat kemudian dia benar lagi.\n\n## Gejala kedua: stok 0 yang nggak ter-update, dan barang yang keburu kebeli\n\nYang bencana itu yang ini: stoknya sudah 0, tapi angkanya nggak ter-update, dan beberapa barang\nkeburu kebeli orang.\n\nDi sini saya baru sadar dua-duanya satu akar, dan akarnya bukan antriannya. Antrian bikin pekerjaan\nselesai **cepat**; antrian nggak bikin pekerjaan selesai **benar**. Aplikasi saya menulis *nilai* —\n\"stok = 4\" — dan nilai itu benar atau salah tergantung kapan dia datang dan kapan dia selesai.\nSelama yang menentukan cuma urutan kedatangan, sistemnya benar karena kebetulan, bukan karena\ndirancang begitu.\n\nAda bentuk kedua yang saya alami juga, di bagian sistem yang lain: dua worker menyentuh baris yang\nsama pada saat yang bersamaan, dan yang menulis belakangan bukan yang datanya paling baru. Gejalanya identik — angka yang tersimpan bukan angka yang benar — dan penyebabnya juga identik: angkanya dihitung di aplikasi, bukan di database.\n\nDan Celery menambahkan satu sifat yang memperparah semuanya: **kalau sebuah task gagal, dia dicoba lagi.** Retry itu fitur, bukan bug. Dia cuma jadi masalah kalau pekerjaannya nggak aman dijalankan\ndua kali.\n\n## Yang saya ubah\n\nDua perubahan, dan dua-duanya satu gagasan: kalau sebuah kejadian punya waktu, jangan biarkan yang\ntua menang.\n\n**1. Update stok ikut membawa waktu event-nya.** Setiap update membawa timestamp dari event-nya, dan cuma update yang lebih baru dari yang sudah tersimpan yang diterapkan. Yang lebih tua ditolak. Jadi\nurutan kedatangan nggak lagi menentukan hasil akhir — yang menentukan adalah waktu kejadiannya.\n\n**2. Mekanismenya dipastikan idempoten.** Setelah itu, retry Celery berhenti jadi hal yang bikin\nsaya deg-degan: mengerjakan ulang pekerjaan yang sama nggak mengubah apa pun. Ini bagian yang paling sering dilewatkan orang, karena retry terasa seperti \"kalau gagal, coba lagi\" — padahal artinya\n\"pekerjaan ini kemungkinan besar dijalankan lebih dari sekali, dan itu harus aman\".\n\n**3. Panggilan ke storefront pakai retry, dan penerimanya idempotent.** Storefront-nya backend yang\nbeda, jadi arahnya push: kita yang memanggil mereka. Panggilan itu sering gagal, dan jawabannya\nadalah retry — yang cuma aman kalau sisi penerimanya bisa menerima panggilan yang sama dua kali\ntanpa mengubah apa pun. Untungnya di situ memang sudah begitu.\n\n## Hasilnya, dan yang saya bawa sampai sekarang\n\nSistemnya masih jalan sampai sekarang, sudah dua tahun, dan sudah lewat banyak update.\n\nSoal skala, ini bagian yang angkanya saya sebut dari ingatan, bukan dari dashboard: sekitar **24 queue** yang jalan, rata-rata **8 worker per queue** — ada yang cuma 1 untuk notifikasi, ada yang sampai 24. Dulu saya pernah menyebut puluhan ribu task per jam; kalau semuanya dihitung, seingat saya angkanya bisa **jutaan task per jam**.\n\nTiga hal yang saya bawa dari kejadian itu:\n\n- **Alat yang benar untuk masalah yang benar.** Keputusan paling mahal dalam cerita ini bukan bug\n  stoknya. Keputusan paling mahal adalah memutuskan membangun pub/sub sendiri. Sekarang, sebelum\n  membangun sesuatu dari nol, saya selalu mulai dengan mencari tahu siapa yang sudah selesai\n  membangunnya.\n- **Retry itu keadaan normal, bukan tanda sistemnya rusak.** Di sistem yang punya antrian, pekerjaan\n  yang sama akan dikerjakan lebih dari sekali. Itu bukan kalimat soal kemungkinan — itu kepastian\n  yang cuma menunggu waktu.\n- **Yang harus kamu pastikan bukan \"jangan sampai dobel\", tapi \"dobel itu nggak mengubah apa pun\".**\n  Yang pertama mustahil dijamin. Yang kedua cuma butuh satu keputusan, dan keputusan itu soal\n  **siapa yang memutuskan sebuah pekerjaan sudah pernah diterapkan** — aplikasimu, atau database.\n\n## Tiga cara kejadian yang sama sampai ke kamu\n\nKalau saya rapikan pelajaran di atas, bentuknya jadi tiga sudut. Stok gudang tadi cuma salah satunya:\n\n1. **Datang lebih dari sekali.** Gateway mengirim ulang karena jawabannya nggak sampai. Ini yang\n   saya kira satu-satunya masalah sampai saya kena bentuk berikutnya —\n   [lab-nya ada di sini](/p/saldo-naik-empat-kali-untuk-satu-pembayaran), lengkap dengan angka hasil\n   pengukuran tiap perbaikan.\n2. **Datang dari dua jalur.** Webhook yang telat, plus job rekonsiliasi pagi yang keburu menutup hari\n   itu: dua kunci, satu fakta, dan nggak ada satu pun dari dua jalur itu yang tahu jalur satunya ada.\n   [Sudut ini juga sudah jadi lab](/p/webhook-telat-job-rekonsiliasi-keburu-jalan).\n3. **Datang tidak berurutan.** Update yang datang belakangan membawa data yang lebih tua, dan yang\n   lebih tua menang. Sudut ini yang saya alami sendiri di stok gudang.\n\nTiga-tiganya akarnya satu: keputusan \"kejadian ini sudah pernah diterapkan atau belum\" diambil\nberdasarkan kedatangan pesannya, di aplikasi. Selama itu belum dipindahkan ke tempat yang bisa\nmemutuskan dengan benar — database, dengan kunci dan patokan waktu yang dimiliki faktanya sendiri —\nkamu akan ketemu sudut keempat, kelima, dan seterusnya.\n\nDua lab yang sudah ada adalah repo yang bisa kamu jalankan sendiri di laptop, dan dua-duanya punya\ngaris akhir yang diperiksa mesin, bukan diperiksa perasaan.\n",[63],{"authors_id":64},{"name":65,"slug":66,"role":67,"profile_picture":48},"Daffa Izzuddin","izzudd","Writer",{"id":69,"title":8,"slug":9,"articles":70,"parent_series":48},"9",[71,81,93,103,111,122,132,142,150,161,169,178,184,194,202,211,220,227,236,245,252,261,268,277,284,292,299,307,314,322,330,340,349,357,365,373,384,392,394,401],{"id":72,"title":73,"slug":74,"excerpt":75,"thumbnail_image":48,"tags":76},"107","Cara kamu pakai AI lebih menentukan daripada apakah kamu pakai AI","cara-pakai-ai-menentukan-pemahaman-kode","Fiturnya selesai, test-nya hijau, dan dua hari kemudian kamu baca kodenya kayak kode orang lain. Ternyata ada satu cara pakai AI yang bikin kamu paham lebih baik daripada nggak pakai AI sama sekali. Yuk lihat datanya.",[77,78,79,80],"ai","belajar","kognitif","ai-assisted-coding",{"id":82,"title":83,"slug":84,"excerpt":85,"thumbnail_image":48,"tags":86},"113","Garbage Collector dituduh bikin aplikasi lemot. Seberapa adil tuduhan itu?","garbage-collector-dituduh-bikin-aplikasi-lemot","GC sering jadi tersangka utama tiap aplikasi melambat, dan banyak yang percaya dia jalan pakai timer tiap beberapa detik. Dua-duanya nggak sepenuhnya benar. Yuk bedah cara kerja GC dari dalam, plus satu pengecualian yang bikin Discord kesakitan tiap 2 menit. 🤔",[87,88,89,90,91,92],"backend","performance","golang","jvm","garbage-collector","memory",{"id":94,"title":95,"slug":96,"excerpt":97,"thumbnail_image":48,"tags":98},"110","Ketika Pilih Framework Cuma Biar Kelihatan Beda","ketika-pilih-framework-cuma-biar-kelihatan-beda","Terakhir kali kamu milih framework atau library baru, itu karena masalahmu butuh alat itu, atau karena alatnya kedengaran keren dan belum banyak yang pakai? Pertanyaan ini nggak enak dijawab. Soalnya keputusan teknologi sering diambil dengan alasan yang nggak pernah ditulis di dokumen teknis mana pun.",[99,100,101,102],"karier","framework","opini","pengalaman",{"id":104,"title":105,"slug":106,"excerpt":107,"thumbnail_image":48,"tags":108},"109","State of JS 2025 Bilang Developer Cuma Pakai 2,6 Framework Sepanjang Karier","state-of-js-2025-developer-cuma-pakai-2-6-framework","Coba hitung semua framework yang pernah kamu coba, lalu potong jadi yang beneran dipakai buat kerjaan. Daftarnya jauh lebih pendek dari dugaan. State of JS 2025 nanya ke 13.002 developer, dan rata-ratanya cuma 2,6. Yuk, lihat angkanya bareng-bareng!",[109,110,100,99],"javascript","survei",{"id":112,"title":113,"slug":114,"excerpt":115,"thumbnail_image":48,"tags":116},"111","Rust Masuk Kernel Linux: Revolusi Nyata atau Cuma Euforia RIIR?","rust-di-kernel-linux-revolusi-atau-cuma-hype","Selama lebih dari 30 tahun kernel Linux cuma mau bahasa C, lalu tiba-tiba Rust resmi masuk di Linux 6.1. Itu revolusi beneran atau cuma kebawa euforia meme Rewrite It In Rust? Yuk lihat datanya, bukan cuma opininya.",[117,118,119,120,121],"rust","linux","kernel","system-programming","memory-safety",{"id":123,"title":124,"slug":125,"excerpt":126,"thumbnail_image":48,"tags":127},"29","Memahami Database Indexing: Rahasia Query Super Cepat","memahami-database-indexing-rahasia-query-super-cepat","Query kalian mulai lelet padahal data baru nembus jutaan baris? Sebelum buru-buru *upgrade* server, yuk cek dulu *index*-nya. Yuk, kita bedah gimana *database indexing* sebenernya kerja!",[128,129,130,131,88,87],"database","sql","postgresql","indexing",{"id":133,"title":134,"slug":135,"excerpt":136,"thumbnail_image":48,"tags":137},"30","Berkenalan dengan Bahasa Go: Kenapa Banyak Dipakai untuk Backend Modern?","berkenalan-dengan-bahasa-go-untuk-backend-modern","Kok bisa sih Go yang masih muda ini jadi bahasa andalan Docker sampai Kubernetes? Yuk, kita kenalan sama Goroutine, Channel, dan alasan dia hemat memori luar biasa!",[89,138,87,139,140,141],"go","concurrency","goroutine","system-design",{"id":143,"title":144,"slug":145,"excerpt":146,"thumbnail_image":48,"tags":147},"11","Apa Kabar JavaScript? Rilis State of JS 2022","apa-kabar-javascript-rilis-state-of-js-2022","Survei State of JS 2022 resmi dirilis dengan lonjakan responden yang luar biasa! Framework dan build tool apa saja yang berhasil mencuri perhatian tahun ini?",[109,148,149],"web","news",{"id":151,"title":152,"slug":153,"excerpt":154,"thumbnail_image":48,"tags":155},"37","Kenapa Backend Butuh Graceful Shutdown? Jangan Asal Cabut Kabel Server!","kenapa-backend-butuh-graceful-shutdown-jangan-asal-cabut-kabel-server","Pernah nggak sih, abis kalian nge-*deploy* backend, beberapa detik kemudian monitoring malah banjir *alert* **502 Bad Gateway** dari pengguna? Saldo udah kepotong tapi status order belum keburu ter-*update* di *database*. Yuk, kita bahas kenapa *graceful shutdown* itu wajib banget!",[87,156,157,158,159,141,160],"devops","fastapi","docker","kubernetes","architecture",{"id":162,"title":163,"slug":164,"excerpt":165,"thumbnail_image":48,"tags":166},"39","Database Sharding vs Partitioning: Kapan Data Perlu Dipecah?","database-sharding-vs-partitioning-kapan-data-perlu-dipecah","Tabel transaksi kalian udah nembus miliaran baris dan tiap *query* laporan bikin *CPU* melonjak 100%? Sebelum asal pecah data, yuk kita pahami dulu beda *sharding* sama *partitioning*!",[87,128,130,141,160,167,168],"sharding","scaling",{"id":170,"title":171,"slug":172,"excerpt":173,"thumbnail_image":48,"tags":174},"24","Kenapa Butuh Rate Limiting?","kenapa-butuh-rate-limiting","Pernah nggak sih endpoint login kalian diserbu bot yang nyoba nembak ribuan password cuma dalam semenit? Yuk, kita bahas kenapa Rate Limiting itu wajib, plus cara masangnya di FastAPI!",[87,175,141,176,177,157],"rate-limiting","api","redis",{"id":179,"title":180,"slug":181,"excerpt":182,"thumbnail_image":48,"tags":183},"38","Optimistic vs Pessimistic Locking: Mengatasi Rebutan Data di Database","optimistic-vs-pessimistic-locking-mengatasi-rebutan-data-di-database","Seribu orang ngeklik \"Beli Sekarang\" di detik yang sama buat rebutan 1 tiket terakhir. Kira-kira apa yang bakal terjadi di server kalian? Yuk, kita banding *pessimistic* sama *optimistic locking*!",[87,128,139,130,141,160,129],{"id":185,"title":186,"slug":187,"excerpt":188,"thumbnail_image":48,"tags":189},"26","REST API vs gRPC: Kapan Sebaiknya Mulai Pindah dari JSON?","rest-api-vs-grpc-kapan-sebaiknya-pindah-dari-json","Pernah nggak sih sistem mikroservis kalian mulai kerasa berat padahal cuma tuker-tukeran data internal? Belum tentu masalah servernya. Yuk, kita bahas kapan REST + JSON masih juara dan kapan gRPC jadi jawabannya!",[87,190,191,192,193,141],"grpc","rest-api","microservices","protobuf",{"id":195,"title":196,"slug":197,"excerpt":198,"thumbnail_image":48,"tags":199},"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!",[120,117,200,201,121],"zig","c",{"id":203,"title":204,"slug":205,"excerpt":206,"thumbnail_image":48,"tags":207},"23","Kenapa Pakai FastAPI dan Kenapa Dia Kenceng Banget?","kenapa-pakai-fastapi-dan-kenapa-dia-kenceng-banget","Sering dengar FastAPI tapi masih bingung kenapa dia bisa ngebut kayak NodeJS dan Go? Yuk, kita bedah bareng rahasia ASGI, sihir Pydantic, sampai dokumentasi otomatisnya!",[157,208,87,209,210],"python","asgi","pydantic",{"id":212,"title":213,"slug":214,"excerpt":215,"thumbnail_image":48,"tags":216},"14","Instalasi Java dengan Visual Studio Code","instalasi-java-dengan-visual-studio-code","Mau belajar ngoding Java tapi spek laptop pas-pasan buat buka IDE berat? Yuk, cari tahu cara setup VS Code biar ngoding Java tetap lancar dan enteng!",[217,218,219],"howto","java","editor",{"id":221,"title":222,"slug":223,"excerpt":224,"thumbnail_image":48,"tags":225},"15","Kenapa Ada Banyak Sekali Distro Linux?","kenapa-ada-banyak-sekali-distro-linux","Pernah bingung pas mau install Linux tapi malah disuguhi puluhan pilihan distro? Tenang, kalian nggak sendirian. Yuk, cari tahu kenapa distro Linux bisa sebanyak itu dan apa bedanya!",[118,226],"distro",{"id":228,"title":229,"slug":230,"excerpt":231,"thumbnail_image":48,"tags":232},"31","Mengenal Redis: Lebih dari Sekadar In-Memory Cache","mengenal-redis-lebih-dari-sekadar-in-memory-cache","Masih nganggep Redis cuma buat caching? Padahal dia bisa jadi leaderboard, message broker, sampai rate limiter lho. Yuk, kita bedah semua kemampuannya!",[177,87,233,234,235,208],"cache","nosql","in-memory",{"id":237,"title":238,"slug":239,"excerpt":240,"thumbnail_image":48,"tags":241},"20","Tailwind v3.0 Rilis: Ada JIT, Arbitrary Values, dan Banyak Lagi!","tailwind-v3-0-rilis-jit-arbitrary-dan-banyak-lagi","Tailwind CSS v3.0 resmi dirilis dengan segudang peningkatan performa yang bikin proses coding makin asyik. Apa aja sih fitur andalan yang wajib dicoba?",[242,243,244],"tailwind-css","css-framework","web-development",{"id":246,"title":247,"slug":248,"excerpt":249,"thumbnail_image":48,"tags":250},"18","React, Angular, atau Vue: Pilih Framework yang Mana?","react-angular-vue-pilih-framework-yang-mana","Bingung menentukan pilihan di antara React, Angular, dan Vue untuk proyek web kalian berikutnya? Tenang, yuk kita bandingkan kelebihan masing-masing di sini!",[244,251,100],"frontend",{"id":253,"title":254,"slug":255,"excerpt":256,"thumbnail_image":48,"tags":257},"19","Static Typing dan Dynamic Typing: Apa Bedanya?","static-typing-dan-dynamic-typing-apa-bedanya","Pernah bingung kenapa di Java tipe data harus dideklarasikan, sementara di Python tinggal pakai aja? Yuk, cari tahu perbedaan static typing dan dynamic typing di sini!",[258,259,260],"programming","types","comparison",{"id":262,"title":263,"slug":264,"excerpt":265,"thumbnail_image":48,"tags":266},"21","TypeScript: JavaScript dengan Gaya dan Tipe Data Statis","typescript-javascript-dengan-gaya","Pernah pusing gara-gara runtime error di JavaScript? Yuk, kenalan dengan TypeScript yang bawa fitur static typing biar kode kalian makin rapi dan aman!",[267,109,244],"typescript",{"id":269,"title":270,"slug":271,"excerpt":272,"thumbnail_image":48,"tags":273},"22","Yang Baru di Nuxt 3: Nitro Engine, Vite, dan Fitur Keren Lainnya","yang-baru-di-nuxt-3-nitro-vite-dan-banyak-lagi","Nuxt 3 hadir membawa perubahan arsitektur besar-besaran untuk menyelaraskan dengan Vue 3. Penasaran apa saja peningkatan performa dan fitur barunya?",[274,275,276],"nuxt-3","vue-3","frontend-framework",{"id":278,"title":279,"slug":280,"excerpt":281,"thumbnail_image":48,"tags":282},"112","Aplikasimu lambat, dan kamu udah kepikiran ganti bahasa. Tunggu dulu","aplikasimu-lambat-dan-kamu-udah-kepikiran-ganti-bahasa-tunggu-dulu","Ada endpoint yang grafiknya spike tiap beberapa menit, dan kepikiran buat pindah dari Go ke Rust? Discord dan Cloudflare beneran melakukannya — tapi cuma setelah kehabisan cara lain dan punya datanya. Yuk, bedah gejala mana yang beneran butuh rewrite, dan mana yang cuma butuh profiling. 🤔",[87,88,89,117,141,283],"profiling",{"id":285,"title":286,"slug":287,"excerpt":288,"thumbnail_image":48,"tags":289},"32","Docker Compose untuk Developer Santai: Satu Perintah Buat Jalankan Semua Service","docker-compose-untuk-developer-santai","Pernah nggak kalian ngulang-ngulang perintah `docker run` cuma buat nyalain web, database, dan cache satu-satu? Capek, kan? Yuk, kita rapikan semuanya jadi satu file dan satu perintah!",[158,290,156,291,87],"docker-compose","containers",{"id":293,"title":294,"slug":295,"excerpt":296,"thumbnail_image":48,"tags":297},"35","Mengenal Idempotency Key: Rahasia Anti-Double Charge di API Pembayaran","mengenal-idempotency-key-rahasia-anti-double-charge-di-api-pembayaran","Pernah internet kalian ngadat pas lagi nekan tombol Bayar, terus panik ngekliknya tiga kali? Kalau endpoint-nya nggak idempotent, saldo bisa kepotong berkali-kali. Yuk, kenalan sama Idempotency Key!",[87,176,54,141,298,157,177],"payment",{"id":300,"title":301,"slug":302,"excerpt":303,"thumbnail_image":48,"tags":304},"25","Kenapa Event Queue Bikin Backend Kalian Nggak Gampang Tumbang?","kenapa-event-queue-bikin-backend-kamu-nggak-gampang-tumbang","Pernah klik tombol Beli Sekarang dan notifikasi suksesnya muncul dalam sekejap, padahal di belakang layar ada invoice, email, sampai push notification yang harus dibuat? Rahasianya Event Queue. Yuk, kita bedah!",[87,141,305,306,160],"event-queue","message-broker",{"id":308,"title":309,"slug":310,"excerpt":311,"thumbnail_image":48,"tags":312},"36","Mencegah Efek Domino di Mikroservis dengan Circuit Breaker Pattern","mencegah-efek-domino-di-mikroservis-dengan-circuit-breaker-pattern","Pernah nggak sih, satu fitur minor di aplikasi kalian mendadak lelet, eh malah bikin halaman checkout dan login ikut down total? Itu namanya *cascading failure*. Yuk, kita bahas gimana *circuit breaker pattern* (pemutus arus) nyelametin arsitektur kalian!",[87,192,313,141,160],"circuit-breaker",{"id":315,"title":316,"slug":317,"excerpt":318,"thumbnail_image":48,"tags":319},"17","Pemrograman Asynchronous vs Multithreading, Apa Sih Bedanya?","pemrograman-asynchronous-dan-multithreading-apa-bedanya","Sering dengar istilah asynchronous dan multithreading pas ngoding tapi masih bingung bedanya? Yuk, kita bedah perbedaannya lewat analogi bikin mi instan yang gampang dipahami!",[320,139,321],"pemrograman","tips-coding",{"id":323,"title":324,"slug":325,"excerpt":326,"thumbnail_image":48,"tags":327},"16","Menggunakan TypeScript dengan Node.js dan Express","menggunakan-typescript-dengan-node-js-dan-express","Pengen bikin server Express di Node.js tapi pakai fitur-fitur keren TypeScript? Yuk, intip cara setup lengkapnya dari nol di sini!",[109,328,329,267],"nodejs","express",{"id":331,"title":332,"slug":333,"excerpt":334,"thumbnail_image":48,"tags":335},"40","Menyelami Distributed Tracing: Melacak Jejak Request di Balik Keruwetan Microservices","menyelami-distributed-tracing-melacak-jejak-request-microservices","Satu klik Checkout di backend kalian bisa memicu puluhan network call antar-service, tapi pas ada yang lambat, service mana sebenernya yang salah? Log aja nggak cukup buat njawab itu. Yuk, kita bedah Distributed Tracing!",[87,336,337,338,192,160,339],"distributed-systems","observability","open-telemetry","tracing",{"id":341,"title":342,"slug":343,"excerpt":344,"thumbnail_image":48,"tags":345},"28","Mengatasi N+1 Query Problem: Musuh Tersembunyi Performa Backend","mengatasi-n-plus-1-query-problem-musuh-tersembunyi-backend","Pernah bikin endpoint yang cuma nampilin 50 postingan beserta nama penulisnya, tapi response-nya lambat banget? Bisa jadi kalian kena N+1 Query. Yuk, kita bedah penyebabnya dan cara memberantasnya!",[128,129,346,347,348,88,87],"orm","sqlalchemy","django",{"id":350,"title":351,"slug":352,"excerpt":353,"thumbnail_image":48,"tags":354},"13","Hal yang Perlu Diperhatikan Sebelum Pindah ke Linux","hal-yang-perlu-diperhatikan-sebelum-pindah-ke-linux","Tertarik migrasi dari Windows ke Linux tapi masih ragu? Yuk, pelajari kelebihan, kekurangan, dan persiapan penting sebelum kamu benar-benar pindah!",[118,355,356],"windows","foss",{"id":358,"title":359,"slug":360,"excerpt":361,"thumbnail_image":48,"tags":362},"27","Background Worker & Task Queue: Jangan Jalankan Proses Berat di Request HTTP!","background-worker-task-queue-jangan-jalankan-proses-berat-di-http","Pernah nggak sih kalian klik Daftar Akun lalu halaman loading muter sampai 15 detik tanpa kepastian? Penyebabnya klasik banget. Yuk, kita bahas kenapa proses berat nggak boleh dijalanin di request HTTP!",[87,363,56,157,57,177,364],"task-queue","async",{"id":366,"title":367,"slug":368,"excerpt":369,"thumbnail_image":48,"tags":370},"12","Buat PowerShell Lebih Berwarna dengan Starship","buat-powershell-lebih-berwarna-dengan-starship","Bosen sama tampilan PowerShell yang hitam putih dan kaku? Yuk, sulap terminalmu jadi lebih interaktif, estetik, dan berwarna pakai Starship!",[371,355,372],"shell","powershell",{"id":374,"title":375,"slug":376,"excerpt":377,"thumbnail_image":48,"tags":378},"41","Log Aggregation Modern dengan Grafana Loki dan Vector: Hemat Storage Tanpa Indeks Raksasa","log-aggregation-modern-grafana-loki-vector-hemat-storage","Klaster Elasticsearch kalian rakus RAM dan boros storage, ya? Padahal nggak semua kata di log perlu diindeks, lho. Yuk, kita kenalan sama duet Grafana Loki dan Vector yang jauh lebih ramah kantong!",[87,379,380,381,382,383,156,141],"observa","logging","grafana","loki","vector",{"id":385,"title":386,"slug":387,"excerpt":388,"thumbnail_image":48,"tags":389},"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!",[87,141,175,177,390,160,391],"api-gateway","traffict-management",{"id":49,"title":50,"slug":51,"excerpt":52,"thumbnail_image":48,"tags":393},[54,55,56,57,58],{"id":395,"title":396,"slug":397,"excerpt":398,"thumbnail_image":48,"tags":399},"34","Kenapa Backend Butuh Database Connection Pooling?","kenapa-backend-butuh-database-connection-pooling","Pernah kena error Too many connections padahal CPU server masih santai di angka 30%? Masalahnya bukan di query kalian, tapi di cara aplikasi ngobrol sama database. Yuk, kenalan sama Connection Pooling!",[87,128,130,400,141,88],"connection-pooling",{"id":402,"title":403,"slug":404,"excerpt":405,"thumbnail_image":48,"tags":406},"33","Berkenalan dengan RabbitMQ: Si Kurir Pesan Serba Bisa","berkenalan-dengan-rabbitmq-si-kurir-pesan-serba-bisa","Pernah nggak sih kalian klik Bayar Sekarang dan notifikasi suksesnya muncul seketika, padahal email konfirmasi baru nyampe beberapa detik kemudian? Rahasianya ada di satu kurir tak terlihat. Yuk, kita kenalan sama RabbitMQ!",[57,306,87,192,364,407],"queue","\u003Cp>Tugas pertama saya di kerjaan pertama bukan bikin fitur. Sistemnya sudah jalan, menerima\nnotifikasi dari luar lewat webhook, dan ditangani langsung oleh handler di aplikasi Laravel-nya.\nSelama sepi nggak ada masalah.\u003C/p>\n\u003Cp>Begitu rame, BOOM, sistemnya kewalahan dan sebagian request-nya di-drop. Request datang lebih cepat\ndaripada yang bisa dikerjakan, dan yang nggak sempat dikerjakan hilang begitu saja — nggak ada\nyang menyimpannya untuk dicoba lagi nanti.\u003C/p>\n\u003Ch2 id=\"keputusan-pertama-saya-cukup-mahal\">Keputusan pertama saya, cukup mahal\u003C/h2>\n\u003Cp>Saya memutuskan membangun pub/sub-nya sendiri dari nol, pakai RabbitMQ dan pika. Alasannya waktu\nitu masuk akal, dan kayaknya semua orang di posisi saya akan punya alasan yang sama: \u003Cstrong>kontrol\u003C/strong>.\nSaya mau tahu persis apa yang terjadi di antara pesan datang dan pekerjaan selesai.\u003C/p>\n\u003Cp>Yang saya dapat: pekerjaan yang tidak selesai. Antrian, reconnect, ack, retry, urutan, semuanya\nharus saya tulis sendiri — dan setiap bagian yang belum saya tulis adalah bagian yang belum jalan.\nSaya capek sendiri, dan sistemnya nggak pernah sempat saya deploy.\u003C/p>\n\u003Cp>Kalau kamu sedang di posisi itu, satu hal yang saya harap kamu percaya tanpa harus kena dulu: yang\nsedang kamu bangun bukan &quot;sistem antrian&quot;. Yang sedang kamu bangun adalah \u003Cstrong>seluruh daftar masalah\nyang orang lain sudah selesaikan\u003C/strong>, dan kamu akan ketemu satu per satu, di produksi, sendirian.\u003C/p>\n\u003Ch2 id=\"yang-akhirnya-dipakai\">Yang akhirnya dipakai\u003C/h2>\n\u003Cp>Saya belajar Celery dan RabbitMQ, lalu memasangnya di depan FastAPI. Di situ semuanya jadi enak:\npekerjaan dikirim ke queue, worker mengerjakan, dan kalau gagal, ya dicoba lagi. Yang tadinya\nberminggu-minggu jadi beberapa hari, dengan kode yang jauh lebih sedikit.\u003C/p>\n\u003Cp>Dan di situ juga saya salah sangka sekali lagi: saya pikir masalahnya selesai. Beberapa waktu\nmemang iya.\u003C/p>\n\u003Ch2 id=\"gejala-pertama-stok-yang-ketinggalan-beberapa-langkah\">Gejala pertama: stok yang ketinggalan beberapa langkah\u003C/h2>\n\u003Cp>Lalu muncul laporan soal stok yang nggak sesuai. Bentuknya begini: masuk dua update untuk barang\nyang sama — stok 2, lalu stok 0. Hasil akhirnya harusnya 0.\u003C/p>\n\u003Cp>Yang terjadi: 2 Worker mengerjakan update, yang 0 duluan, selesai, lalu update yang 2 datang dan\nmenimpa angkanya. Nggak ada satu pun dari dua update itu yang salah; yang salah cuma urutannya. Hasilnya? barangnya zero stock tapi masih bisa dibeli.\u003C/p>\n\u003Cp>Ini belum bencana. Angkanya telat, dan beberapa saat kemudian dia benar lagi.\u003C/p>\n\u003Ch2 id=\"gejala-kedua-stok-0-yang-nggak-ter-update-dan-barang-yang-keburu-kebeli\">Gejala kedua: stok 0 yang nggak ter-update, dan barang yang keburu kebeli\u003C/h2>\n\u003Cp>Yang bencana itu yang ini: stoknya sudah 0, tapi angkanya nggak ter-update, dan beberapa barang\nkeburu kebeli orang.\u003C/p>\n\u003Cp>Di sini saya baru sadar dua-duanya satu akar, dan akarnya bukan antriannya. Antrian bikin pekerjaan\nselesai \u003Cstrong>cepat\u003C/strong>; antrian nggak bikin pekerjaan selesai \u003Cstrong>benar\u003C/strong>. Aplikasi saya menulis \u003Cem>nilai\u003C/em> —\n&quot;stok = 4&quot; — dan nilai itu benar atau salah tergantung kapan dia datang dan kapan dia selesai.\nSelama yang menentukan cuma urutan kedatangan, sistemnya benar karena kebetulan, bukan karena\ndirancang begitu.\u003C/p>\n\u003Cp>Ada bentuk kedua yang saya alami juga, di bagian sistem yang lain: dua worker menyentuh baris yang\nsama pada saat yang bersamaan, dan yang menulis belakangan bukan yang datanya paling baru. Gejalanya identik — angka yang tersimpan bukan angka yang benar — dan penyebabnya juga identik: angkanya dihitung di aplikasi, bukan di database.\u003C/p>\n\u003Cp>Dan Celery menambahkan satu sifat yang memperparah semuanya: \u003Cstrong>kalau sebuah task gagal, dia dicoba lagi.\u003C/strong> Retry itu fitur, bukan bug. Dia cuma jadi masalah kalau pekerjaannya nggak aman dijalankan\ndua kali.\u003C/p>\n\u003Ch2 id=\"yang-saya-ubah\">Yang saya ubah\u003C/h2>\n\u003Cp>Dua perubahan, dan dua-duanya satu gagasan: kalau sebuah kejadian punya waktu, jangan biarkan yang\ntua menang.\u003C/p>\n\u003Cp>\u003Cstrong>1. Update stok ikut membawa waktu event-nya.\u003C/strong> Setiap update membawa timestamp dari event-nya, dan cuma update yang lebih baru dari yang sudah tersimpan yang diterapkan. Yang lebih tua ditolak. Jadi\nurutan kedatangan nggak lagi menentukan hasil akhir — yang menentukan adalah waktu kejadiannya.\u003C/p>\n\u003Cp>\u003Cstrong>2. Mekanismenya dipastikan idempoten.\u003C/strong> Setelah itu, retry Celery berhenti jadi hal yang bikin\nsaya deg-degan: mengerjakan ulang pekerjaan yang sama nggak mengubah apa pun. Ini bagian yang paling sering dilewatkan orang, karena retry terasa seperti &quot;kalau gagal, coba lagi&quot; — padahal artinya\n&quot;pekerjaan ini kemungkinan besar dijalankan lebih dari sekali, dan itu harus aman&quot;.\u003C/p>\n\u003Cp>\u003Cstrong>3. Panggilan ke storefront pakai retry, dan penerimanya idempotent.\u003C/strong> Storefront-nya backend yang\nbeda, jadi arahnya push: kita yang memanggil mereka. Panggilan itu sering gagal, dan jawabannya\nadalah retry — yang cuma aman kalau sisi penerimanya bisa menerima panggilan yang sama dua kali\ntanpa mengubah apa pun. Untungnya di situ memang sudah begitu.\u003C/p>\n\u003Ch2 id=\"hasilnya-dan-yang-saya-bawa-sampai-sekarang\">Hasilnya, dan yang saya bawa sampai sekarang\u003C/h2>\n\u003Cp>Sistemnya masih jalan sampai sekarang, sudah dua tahun, dan sudah lewat banyak update.\u003C/p>\n\u003Cp>Soal skala, ini bagian yang angkanya saya sebut dari ingatan, bukan dari dashboard: sekitar \u003Cstrong>24 queue\u003C/strong> yang jalan, rata-rata \u003Cstrong>8 worker per queue\u003C/strong> — ada yang cuma 1 untuk notifikasi, ada yang sampai 24. Dulu saya pernah menyebut puluhan ribu task per jam; kalau semuanya dihitung, seingat saya angkanya bisa \u003Cstrong>jutaan task per jam\u003C/strong>.\u003C/p>\n\u003Cp>Tiga hal yang saya bawa dari kejadian itu:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>Alat yang benar untuk masalah yang benar.\u003C/strong> Keputusan paling mahal dalam cerita ini bukan bug\nstoknya. Keputusan paling mahal adalah memutuskan membangun pub/sub sendiri. Sekarang, sebelum\nmembangun sesuatu dari nol, saya selalu mulai dengan mencari tahu siapa yang sudah selesai\nmembangunnya.\u003C/li>\n\u003Cli>\u003Cstrong>Retry itu keadaan normal, bukan tanda sistemnya rusak.\u003C/strong> Di sistem yang punya antrian, pekerjaan\nyang sama akan dikerjakan lebih dari sekali. Itu bukan kalimat soal kemungkinan — itu kepastian\nyang cuma menunggu waktu.\u003C/li>\n\u003Cli>\u003Cstrong>Yang harus kamu pastikan bukan &quot;jangan sampai dobel&quot;, tapi &quot;dobel itu nggak mengubah apa pun&quot;.\u003C/strong>\nYang pertama mustahil dijamin. Yang kedua cuma butuh satu keputusan, dan keputusan itu soal\n\u003Cstrong>siapa yang memutuskan sebuah pekerjaan sudah pernah diterapkan\u003C/strong> — aplikasimu, atau database.\u003C/li>\n\u003C/ul>\n\u003Ch2 id=\"tiga-cara-kejadian-yang-sama-sampai-ke-kamu\">Tiga cara kejadian yang sama sampai ke kamu\u003C/h2>\n\u003Cp>Kalau saya rapikan pelajaran di atas, bentuknya jadi tiga sudut. Stok gudang tadi cuma salah satunya:\u003C/p>\n\u003Col>\n\u003Cli>\u003Cstrong>Datang lebih dari sekali.\u003C/strong> Gateway mengirim ulang karena jawabannya nggak sampai. Ini yang\nsaya kira satu-satunya masalah sampai saya kena bentuk berikutnya —\n\u003Ca href=\"/p/saldo-naik-empat-kali-untuk-satu-pembayaran\">lab-nya ada di sini\u003C/a>, lengkap dengan angka hasil\npengukuran tiap perbaikan.\u003C/li>\n\u003Cli>\u003Cstrong>Datang dari dua jalur.\u003C/strong> Webhook yang telat, plus job rekonsiliasi pagi yang keburu menutup hari\nitu: dua kunci, satu fakta, dan nggak ada satu pun dari dua jalur itu yang tahu jalur satunya ada.\n\u003Ca href=\"/p/webhook-telat-job-rekonsiliasi-keburu-jalan\">Sudut ini juga sudah jadi lab\u003C/a>.\u003C/li>\n\u003Cli>\u003Cstrong>Datang tidak berurutan.\u003C/strong> Update yang datang belakangan membawa data yang lebih tua, dan yang\nlebih tua menang. Sudut ini yang saya alami sendiri di stok gudang.\u003C/li>\n\u003C/ol>\n\u003Cp>Tiga-tiganya akarnya satu: keputusan &quot;kejadian ini sudah pernah diterapkan atau belum&quot; diambil\nberdasarkan kedatangan pesannya, di aplikasi. Selama itu belum dipindahkan ke tempat yang bisa\nmemutuskan dengan benar — database, dengan kunci dan patokan waktu yang dimiliki faktanya sendiri —\nkamu akan ketemu sudut keempat, kelima, dan seterusnya.\u003C/p>\n\u003Cp>Dua lab yang sudah ada adalah repo yang bisa kamu jalankan sendiri di laptop, dan dua-duanya punya\ngaris akhir yang diperiksa mesin, bukan diperiksa perasaan.\u003C/p>\n","\u003Cul>\n\u003Cli>Antrian bikin pekerjaan selesai cepat; antrian nggak bikin pekerjaan selesai benar.\u003C/li>\n\u003Cli>Update yang datang tidak berurutan bikin nilai lama menang, dan stok gudang yang salah bikin barang keburu kebeli.\u003C/li>\n\u003Cli>Retry itu keadaan normal, bukan tanda sistemnya rusak — jadi yang harus dipastikan bukan &quot;jangan dobel&quot;, tapi &quot;dobel nggak mengubah apa pun&quot;.\u003C/li>\n\u003Cli>Yang menentukan siapa pemilik keputusan itu: aplikasimu, atau database.\u003C/li>\n\u003C/ul>\n",[411,413,415,423,425,427],{"id":82,"title":83,"slug":84,"excerpt":85,"thumbnail_image":48,"tags":412},[87,88,89,90,91,92],{"id":112,"title":113,"slug":114,"excerpt":115,"thumbnail_image":48,"tags":414},[117,118,119,120,121],{"id":416,"title":417,"slug":418,"excerpt":419,"thumbnail_image":48,"tags":420},"114","Saldo Naik Empat Kali untuk Satu Pembayaran","saldo-naik-empat-kali-untuk-satu-pembayaran","Kodenya nggak salah, dan gateway-nya juga nggak salah. Tapi satu pembayaran muncul empat kali di ledger. Coba tebak kenapa — lalu bikin `make check` hijau di lab-nya.",[54,421,422,128,40],"webhook","postgres",{"id":278,"title":279,"slug":280,"excerpt":281,"thumbnail_image":48,"tags":424},[87,88,89,117,141,283],{"id":49,"title":50,"slug":51,"excerpt":52,"thumbnail_image":48,"tags":426},[54,55,56,57,58],{"id":428,"title":429,"slug":430,"excerpt":431,"thumbnail_image":48,"tags":432},"116","Webhook-nya Telat, Job Rekonsiliasinya Keburu Jalan","webhook-telat-job-rekonsiliasi-keburu-jalan","Jalur webhook sudah idempotent. Lalu ada job rekonsiliasi pagi, dan satu pembayaran tercatat dua kali dari dua jalur yang dua-duanya benar. Coba tebak kuncinya salah di mana.",[54,433,421,422,40],"reconciliation",[435,439,442,445,448,451,454],{"text":436,"depth":437,"id":438},"Keputusan pertama saya, cukup mahal",2,"keputusan-pertama-saya-cukup-mahal",{"text":440,"depth":437,"id":441},"Yang akhirnya dipakai","yang-akhirnya-dipakai",{"text":443,"depth":437,"id":444},"Gejala pertama: stok yang ketinggalan beberapa langkah","gejala-pertama-stok-yang-ketinggalan-beberapa-langkah",{"text":446,"depth":437,"id":447},"Gejala kedua: stok 0 yang nggak ter-update, dan barang yang keburu kebeli","gejala-kedua-stok-0-yang-nggak-ter-update-dan-barang-yang-keburu-kebeli",{"text":449,"depth":437,"id":450},"Yang saya ubah","yang-saya-ubah",{"text":452,"depth":437,"id":453},"Hasilnya, dan yang saya bawa sampai sekarang","hasilnya-dan-yang-saya-bawa-sampai-sekarang",{"text":455,"depth":437,"id":456},"Tiga cara kejadian yang sama sampai ke kamu","tiga-cara-kejadian-yang-sama-sampai-ke-kamu",1791651541754]