<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mohamad Hasan</title>
    <description>The latest articles on DEV Community by Mohamad Hasan (@hasan197).</description>
    <link>https://dev.to/hasan197</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3138342%2F18bfd976-9ca0-4083-b08b-5a8c250be4ff.png</url>
      <title>DEV Community: Mohamad Hasan</title>
      <link>https://dev.to/hasan197</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hasan197"/>
    <language>en</language>
    <item>
      <title>Ketika 14.000 Transaksi Membanjiri Server Per Menit: Bagaimana Rate Limiter Cegah Crash</title>
      <dc:creator>Mohamad Hasan</dc:creator>
      <pubDate>Sat, 25 Oct 2025 08:00:40 +0000</pubDate>
      <link>https://dev.to/hasan197/ketika-jutaan-wajib-pajak-membanjiri-server-di-balik-layar-sistem-coretax-3ik4</link>
      <guid>https://dev.to/hasan197/ketika-jutaan-wajib-pajak-membanjiri-server-di-balik-layar-sistem-coretax-3ik4</guid>
      <description>&lt;p&gt;Beberapa kali Server Direktorat Jenderal Pajak kembali mengalami downtime. Bukan untuk pertama kali. Sejak diluncurkan awal tahun ini, sistem Coretax—platform modernisasi perpajakan terbesar dalam sejarah Indonesia—sudah berkali-kali mengalami pemeliharaan mendadak. Di April, sistem mati 18 jam. Di Juni, lagi. Juli, lagi.&lt;/p&gt;

&lt;p&gt;Di ruang kontrol pusat data, layar menampilkan grafik merah menyala: ribuan permintaan per detik menghantam endpoint yang sama. Bagi wajib pajak, ini hanya klik tombol "Lapor SPT". Bagi sistem, ini tsunami digital.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;86,7 Juta Wajib Pajak, Satu Pintu Masuk&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Bayangkan ini: pada akhir 2024, Indonesia mencatat 86,7 juta wajib pajak terdaftar—naik 17,23% dari tahun sebelumnya. Dari jumlah itu, 19,78 juta diwajibkan melaporkan SPT Tahunan. Dan ketika tenggat waktu mendekat—seperti 31 Maret untuk wajib pajak orang pribadi—jutaan orang serentak membuka portal yang sama.&lt;/p&gt;

&lt;p&gt;Data DJP mencatat hingga 1 April 2025, 12,34 juta SPT Tahunan sudah diterima. Itu berarti rata-rata lebih dari 400.000 SPT masuk per hari di minggu terakhir Maret. Dalam hitungan kasar, jika diasumsikan 8 jam kerja per hari, itu sekitar 14.000 transaksi per menit. Dan setiap transaksi tidak sesederhana satu klik—ada login, pengisian formulir, unggah dokumen, submit, hingga notifikasi.&lt;/p&gt;

&lt;p&gt;Tanpa mekanisme pembatas yang cermat, server bisa kolaps. Dan ketika sistem publik kolaps, konsekuensinya bukan sekadar keluhan di media sosial. Ini soal macetnya roda administrasi negara.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Anatomi Rate Limiter: Polisi Lalu Lintas Digital&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Di sinilah rate limiter masuk. Dalam arsitektur IT modern, rate limiter bekerja seperti polisi lalu lintas—mengatur siapa boleh lewat, berapa banyak, dan seberapa cepat. Ia adalah gerbang tol digital yang menjaga agar tidak semua mobil masuk sekaligus.&lt;/p&gt;

&lt;p&gt;Mekanismenya didasarkan pada dua algoritma utama: &lt;strong&gt;Token Bucket&lt;/strong&gt; dan &lt;strong&gt;Leaky Bucket&lt;/strong&gt;. Keduanya punya filosofi berbeda, tapi tujuan sama: menjaga stabilitas sistem di tengah lonjakan trafik.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Token Bucket: Ember dengan Koin&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Bayangkan sebuah ember berisi koin (token). Setiap kali pengguna ingin mengirim permintaan ke server, mereka harus "membayar" dengan satu token dari ember. Jika token tersedia, permintaan diproses. Jika tidak, permintaan ditolak atau ditunda.&lt;/p&gt;

&lt;p&gt;Yang menarik: ember ini terus diisi ulang secara otomatis. Misalnya, setiap detik ditambah 10 token baru, dengan kapasitas maksimum 100 token. Ini berarti sistem bisa menangani &lt;strong&gt;burst traffic&lt;/strong&gt;—lonjakan mendadak—selama masih ada token di ember. Tapi jika semua token habis, pengguna harus menunggu hingga ember terisi kembali.&lt;/p&gt;

&lt;p&gt;Token Bucket memproses permintaan dengan jumlah token yang tersedia secara dinamis, memungkinkan sistem menangani burst traffic sementara selama token masih ada di dalam ember. Inilah kenapa algoritma ini populer di API Gateway seperti NGINX, Kong, atau AWS API Gateway—mereka butuh fleksibilitas untuk menangani pola trafik yang tidak menentu.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contoh konkret:&lt;/strong&gt; Twitter membatasi akun gratis hingga 500 tweet per hari. Itu adalah implementasi Token Bucket. Anda bisa mem-posting 50 tweet sekaligus dalam satu menit (burst), tapi setelah itu harus menunggu token terisi kembali.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Leaky Bucket: Ember yang Bocor&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Algoritma kedua, Leaky Bucket, bekerja berbeda. Bayangkan permintaan masuk seperti air yang dituang ke dalam ember. Permintaan diproses (bocor keluar) dengan kecepatan konstan, seperti air yang menetes dari lubang. Jika permintaan datang terlalu cepat, ember meluap, dan permintaan berlebih diabaikan.&lt;/p&gt;

&lt;p&gt;Bedanya dengan Token Bucket: Leaky Bucket memproses permintaan dengan &lt;strong&gt;kecepatan tetap&lt;/strong&gt;, bukan berdasarkan ketersediaan token. Ini membuatnya lebih stabil dan prediktabel, tapi kurang fleksibel untuk burst traffic.&lt;/p&gt;

&lt;p&gt;Algoritma Leaky Bucket dapat memproses permintaan secara robust karena hanya memproses permintaan dalam jendela waktu yang tetap. Namun, ia memiliki masalah: permintaan yang diproses mungkin bukan yang terbaru. Seperti antrean roller coaster di taman bermain—Anda harus menunggu giliran, bahkan jika antrian sangat panjang.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contoh konkret:&lt;/strong&gt; Router jaringan menggunakan Leaky Bucket untuk mengatur aliran paket data. Paket masuk dengan kecepatan bervariasi, tapi diproses dengan kecepatan konstan untuk mencegah kongesti.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Token vs Leaky: Kapan Pakai yang Mana?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Token Bucket memberikan kontrol granular dengan parameter MAX_CAPACITY dan REFILL_RATE, memungkinkan burst traffic singkat melebihi REFILL_RATE selama token masih tersedia. Sementara Leaky Bucket menghasilkan output konstan yang ditentukan oleh LEAK_RATE, memberikan perilaku prediktabel yang memudahkan pemeliharaan.&lt;/p&gt;

&lt;p&gt;Dalam praktik, Sistem pemerintah cenderung menggunakan &lt;strong&gt;Token Bucket&lt;/strong&gt; karena fleksibilitasnya. Ketika jutaan wajib pajak mengakses sistem di hari terakhir pelaporan, burst traffic tidak bisa dihindari. Token Bucket memungkinkan sistem menangani lonjakan singkat tanpa langsung menolak semua permintaan.&lt;/p&gt;

&lt;p&gt;Tapi untuk aplikasi yang butuh output stabil—seperti streaming video atau processing antrian transaksi—Leaky Bucket lebih cocok.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Implementasi di Dunia Nyata: Dari Startup hingga Negara&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Rate limiter bukan teknologi eksotis. Ia adalah standar industri yang digunakan di mana-mana.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google API&lt;/strong&gt; membatasi permintaan per detik dan per hari. Jika Anda menggunakan Google Maps API versi gratis, Anda hanya bisa melakukan 40.000 permintaan per bulan. Melebihi itu? Sistem mengembalikan error &lt;strong&gt;HTTP 429: Too Many Requests&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub API&lt;/strong&gt; membatasi 5.000 permintaan per jam untuk pengguna yang sudah login. Setiap response header menyertakan informasi: &lt;code&gt;X-RateLimit-Remaining: 4999&lt;/code&gt;, memberitahu berapa permintaan yang masih bisa dilakukan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gojek, Tokopedia, Shopee&lt;/strong&gt;—mereka semua menggunakan rate limiter untuk bertahan saat flash sale. Ketika kampanye 11.11 atau Harbolnas, lonjakan pengguna bisa meledak ribuan kali lipat dalam satu menit. Tanpa rate limiter, server mereka akan kolaps dalam hitungan detik.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Rate Limiter di Layer Gateway: Benteng Pertahanan Pertama&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Dalam arsitektur Coretax, rate limiter bisa diterapkan di &lt;strong&gt;API Gateway&lt;/strong&gt;—pintu masuk utama sebelum permintaan mencapai server aplikasi atau database. Ini adalah benteng pertahanan pertama.&lt;/p&gt;

&lt;p&gt;Ketika pengguna klik "Login" di portal Coretax, permintaan tidak langsung masuk ke database. Ia harus melewati beberapa lapisan:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Load Balancer&lt;/strong&gt; — Membagi trafik ke beberapa server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway&lt;/strong&gt; — Di sinilah rate limiter bekerja&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application Server&lt;/strong&gt; — Memproses logika bisnis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database&lt;/strong&gt; — Menyimpan dan mengambil data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Jika rate limiter tidak ada, semua permintaan—termasuk yang berlebihan—akan langsung menghantam application server dan database. Dalam hitungan menit, sistem bisa crash.&lt;/p&gt;

&lt;p&gt;Dengan rate limiter, sistem bisa membatasi, misalnya:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;100 permintaan per menit per pengguna&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;1.000 permintaan per menit per IP address&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;10.000 permintaan per menit untuk seluruh sistem&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Jika batas terlampaui, API Gateway mengembalikan response: &lt;code&gt;HTTP 429 - Too Many Requests. Retry-After: 60 seconds&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Pengguna mungkin harus menunggu sebentar, tapi setidaknya sistem tidak kolaps total.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Dari Bug hingga Latensi: Perjuangan Menstabilkan Sistem&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Per Mei 2025, mantan Dirjen Pajak Suryo Utomo menyebutkan bahwa DJP masih memperbaiki bug di 18 proses bisnis—turun dari 21 sebelumnya. Target penyelesaian: akhir Juli 2025. Sementara migrasi data dari sistem lama (DJP Online) ke Coretax ditargetkan selesai Desember 2025.&lt;/p&gt;

&lt;p&gt;Tantangannya bukan sekadar teknis. Ini soal ekspektasi. Wajib pajak menginginkan sistem yang cepat dan stabil. Fiskus membutuhkan data real-time untuk pengawasan. Dan DJP harus memastikan bahwa ketika jutaan orang mengakses sistem secara bersamaan—seperti saat tenggat waktu pelaporan SPT—server tidak ambruk.&lt;/p&gt;

&lt;p&gt;Dalam implementasi modern, rate limiter tidak hanya membatasi jumlah permintaan, tapi juga memberi sinyal ke sistem lain. Misalnya:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jika trafik melonjak di atas threshold tertentu, &lt;strong&gt;auto-scaling&lt;/strong&gt; akan menambah server secara otomatis&lt;/li&gt;
&lt;li&gt;Jika ada pengguna yang mencoba melakukan &lt;strong&gt;brute force attack&lt;/strong&gt; (menebak password berkali-kali), rate limiter akan memblokir IP tersebut&lt;/li&gt;
&lt;li&gt;Jika ada aplikasi pihak ketiga yang membanjiri API dengan permintaan berlebihan, rate limiter akan membatasi akses mereka&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ini bukan hanya soal mencegah crash—ini soal keamanan, efisiensi, dan pengalaman pengguna.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Pelajaran dari Flash Sale hingga Pemilu&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Indonesia sebenarnya tidak asing dengan lonjakan trafik ekstrem. Platform e-commerce seperti Tokopedia dan Shopee rutin menghadapi jutaan pengguna serentak saat kampanye 11.11 atau Harbolnas. Mereka bertahan bukan karena server raksasa, tapi karena arsitektur yang cermat—termasuk rate limiting yang ketat.&lt;/p&gt;

&lt;p&gt;Begitu pula dengan sistem pemerintah lain. Online Single Submission (OSS) untuk perizinan usaha, atau sistem pendaftaran CPNS, juga menghadapi pola serupa: ribuan pengguna mengakses sistem dalam waktu singkat. Tanpa pembatas yang tepat, sistem bisa lumpuh dalam hitungan menit.&lt;/p&gt;

&lt;p&gt;Pengalaman global menunjukkan hal yang sama. Saat pemilu di beberapa negara, server registrasi pemilih sering kolaps karena lonjakan trafik. Solusinya bukan menambah server sebanyak mungkin, tapi mengatur arus dengan cerdas—dan itulah fungsi rate limiter.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Paradoks yang Indah: Melambat untuk Tetap Cepat&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Ada ironi dalam arsitektur IT modern: yang menjaga sistem tetap cepat justru alat yang memperlambatnya sedikit. Rate limiter bukan penghambat—ia adalah pengatur ritme. Ia memastikan bahwa sistem tidak kewalahan, sumber daya dibagi adil, dan tidak ada satu pengguna yang membanjiri server sendirian.&lt;/p&gt;

&lt;p&gt;Rate limiter berguna untuk aplikasi yang memerlukan kontrol lebih presisi atas pembatasan rate dan dapat menanggung overhead memori tambahan. Bermanfaat untuk aplikasi yang perlu memproses permintaan dengan kecepatan konsisten, seperti network traffic shaping atau mengontrol operasi write ke disk.&lt;/p&gt;

&lt;p&gt;Di dunia yang terobsesi dengan kecepatan, rate limiter ini relevan: kadang, untuk menjaga kelancaran bersama, kita memang harus rela menunggu sebentar.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Di balik setiap halaman login yang "sedikit lebih lama", mungkin ada mekanisme perlindungan yang sedang bekerja. Rate limiter memastikan bahwa meskipun jutaan orang mengakses sistem yang sama, semuanya tetap mendapatkan giliran.&lt;/strong&gt;&lt;/p&gt;




</description>
    </item>
    <item>
      <title>Ketika Golang Dipilih untuk Kirim Pesan Yang Sibuk</title>
      <dc:creator>Mohamad Hasan</dc:creator>
      <pubDate>Thu, 08 May 2025 16:20:23 +0000</pubDate>
      <link>https://dev.to/hasan197/ketika-golang-dipilih-untuk-kirim-pesan-yang-sibuk-jak</link>
      <guid>https://dev.to/hasan197/ketika-golang-dipilih-untuk-kirim-pesan-yang-sibuk-jak</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbh8kzxutxk0922prs9ko.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbh8kzxutxk0922prs9ko.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bayangkan sebuah kota metropolitan di zaman belum ada telepon dan internet. Ribuan surat datang setiap hari ke kantor pos pusat, dan harus dibagikan ke rumah-rumah atau kantor di seluruh penjuru kota. Jika hanya satu tukang pos yang bertugas, tentu surat akan menumpuk, pengiriman jadi lambat, dan banyak pesan penting yang terlambat sampai. Tapi bagaimana jika ada puluhan, bahkan ratusan tukang pos yang bisa bekerja bersama, saling membantu, dan semuanya bisa berkoordinasi dengan baik? Inilah gambaran sederhana mengapa sistem pengiriman pesan yang canggih membutuhkan teknologi yang tepat.&lt;/p&gt;

&lt;p&gt;Mengapa Golang? Karena Golang seperti pasukan tukang pos super cepat yang bisa bergerak serempak tanpa saling tabrakan. Dalam dunia komputer, ini disebut concurrency—kemampuan melakukan banyak hal sekaligus. Golang punya alat khusus bernama goroutine, seperti tukang pos kecil yang bisa diutus kapan saja untuk mengantarkan surat. Tidak hanya itu, mereka bisa saling mengirim kabar lewat channels, semacam kode-kode atau pesan singkat agar tidak ada yang salah paham atau tersesat di jalan.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Concurrency dengan Goroutines dan Channels
&lt;/h2&gt;

&lt;p&gt;Ketika kota harus mengirim banyak surat ke berbagai penjuru sekaligus, satu-satunya cara agar pesan tidak menumpuk adalah dengan membagi tugas ke banyak tukang pos. Di Golang, setiap "tukang pos" ini disebut goroutine. Mereka bisa diaktifkan kapan saja, jumlahnya bisa sangat banyak, dan mereka tidak saling mengganggu.&lt;/p&gt;

&lt;p&gt;Misalnya, saat kantor pos menerima surat, Golang bisa langsung mengutus goroutine untuk mengantarkannya tanpa harus menunggu tugas lain selesai. Ini seperti mengirim surat ke banyak alamat sekaligus, bukan satu per satu. Dengan channel, setiap goroutine bisa memberi kabar, “Surat sudah sampai!” atau “Ada masalah di jalan!” sehingga koordinasi tetap terjaga.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://go.dev/doc/effective_go#goroutines" rel="noopener noreferrer"&gt;Lihat dokumentasi goroutine&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// MessageProcessor menangani pesan secara concurrent&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;MessageProcessor&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;incomingMessages&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;
    &lt;span class="n"&gt;workers&lt;/span&gt;          &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MessageProcessor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Jalankan multiple workers&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;workers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;processMessages&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MessageProcessor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;processMessages&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;incomingMessages&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Proses setiap pesan dalam goroutine terpisah&lt;/span&gt;
        &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;MessageQueue&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;    &lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;
    &lt;span class="n"&gt;errors&lt;/span&gt;      &lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
    &lt;span class="n"&gt;shutdown&lt;/span&gt;    &lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mq&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MessageQueue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Listen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;mq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="c"&gt;// Proses pesan baru&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Processing message ID: %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;mq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="c"&gt;// Tangani error&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error processing message: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;mq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shutdown&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="c"&gt;// Terima sinyal shutdown&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Shutting down message processor"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tapi, bagaimana jika ada dua tukang pos yang ingin menghitung berapa banyak surat yang sudah dikirim? Kalau tidak hati-hati, catatan bisa jadi berantakan. Di sinilah atomic operation berperan, memastikan setiap penambahan atau pengurangan dilakukan dengan rapi, tanpa rebutan.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pkg.go.dev/sync/atomic" rel="noopener noreferrer"&gt;Lihat dokumentasi sync/atomic&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;MessageStats&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;processed&lt;/span&gt; &lt;span class="n"&gt;atomic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int64&lt;/span&gt;
    &lt;span class="n"&gt;failed&lt;/span&gt;    &lt;span class="n"&gt;atomic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int64&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MessageStats&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;RecordSuccess&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;processed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MessageStats&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;RecordFailure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MessageStats&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;GetStats&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;processed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;failed&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;processed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Message Queue dengan RabbitMQ
&lt;/h2&gt;

&lt;p&gt;Sekarang bayangkan ada lumbung surat besar di tengah kota. Semua surat dari luar kota ditaruh di sana dulu, lalu tukang pos mengambil satu per satu sesuai rute dan giliran. RabbitMQ adalah lumbung surat digital yang memastikan tidak ada surat yang hilang, dan setiap pesan diproses dengan urutan yang benar.&lt;/p&gt;

&lt;p&gt;Dengan bantuan goroutine, surat-surat ini bisa diambil dan diantarkan secara bersamaan, pekerjaan jadi jauh lebih cepat. Setiap pesan yang masuk ke antrian akan diantar oleh tukang pos (worker) yang berjalan dalam goroutine terpisah, memastikan tidak ada penumpukan di satu titik.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Consumer&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;queue&lt;/span&gt;    &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;amqp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Delivery&lt;/span&gt;
    &lt;span class="n"&gt;done&lt;/span&gt;     &lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Consumer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;workerCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;processMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Nack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Kirim kembali ke queue&lt;/span&gt;
                        &lt;span class="k"&gt;continue&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Manajemen perjalanan tukang pos menjadi sangat penting. Sistem harus bisa menambah, menghentikan, dan memantau setiap tukang pos secara dinamis. Heartbeat dan error channel digunakan untuk memastikan setiap tukang pos tetap aktif dan bisa digantikan jika ada yang kelelahan atau tersesat.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;ConsumerHealth&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;lastHeartbeat&lt;/span&gt; &lt;span class="n"&gt;atomic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt;        &lt;span class="n"&gt;atomic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;
    &lt;span class="n"&gt;done&lt;/span&gt;          &lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ConsumerHealth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Monitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ticker&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTicker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updateHealth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isHealthy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unhealthy"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reconnect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Graceful Shutdown
&lt;/h2&gt;

&lt;p&gt;Sistem juga harus tahu kapan harus berhenti bekerja. Ketika hari mulai gelap dan kota harus beristirahat, semua tukang pos harus memastikan tidak ada surat yang tertinggal sebelum pulang. Inilah yang disebut graceful shutdown. Dengan WaitGroup, Golang memastikan semua tugas benar-benar selesai sebelum sistem dimatikan. Tidak ada pesan yang terlewat, tidak ada pengiriman yang ditinggalkan begitu saja.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pkg.go.dev/context" rel="noopener noreferrer"&gt;Lihat dokumentasi context&lt;/a&gt;, &lt;a href="https://pkg.go.dev/sync#WaitGroup" rel="noopener noreferrer"&gt;sync.WaitGroup&lt;/a&gt;, dan &lt;a href="https://go.dev/doc/effective_go#goroutines" rel="noopener noreferrer"&gt;goroutine&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;MessageServer&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;consumers&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Consumer&lt;/span&gt;
    &lt;span class="n"&gt;wg&lt;/span&gt;        &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WaitGroup&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MessageServer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Shutdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Kirim sinyal shutdown ke semua consumer&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;consumer&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;consumers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Consumer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Shutdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error shutting down consumer %s: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;consumer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Tunggu semua consumer selesai atau context timeout&lt;/span&gt;
    &lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}()&lt;/span&gt;

    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dalam implementasi nyata, graceful shutdown diintegrasikan ke seluruh komponen. Shutdown manager akan menangkap sinyal sistem (seperti SIGINT atau SIGTERM), membatalkan context, menutup channel, dan menunggu semua proses selesai dengan WaitGroup. Hasilnya, sistem dapat berhenti dengan mulus tanpa kehilangan data atau meninggalkan proses zombie.&lt;/p&gt;




&lt;h2&gt;
  
  
  Penutup: Apa Pelajaran Besarnya?
&lt;/h2&gt;

&lt;p&gt;Mengapa semua ini penting? Karena dalam dunia nyata, sistem seperti ini harus bisa diandalkan. Tidak boleh ada pesan yang hilang, tidak boleh ada pengiriman yang macet, dan semuanya harus bisa berjalan cepat tanpa saling mengganggu. Golang menyediakan semua alat itu secara sederhana, efisien, dan mudah dipahami.&lt;/p&gt;

&lt;p&gt;Pelajaran terbesar dari perjalanan ini sederhana: teknologi terbaik adalah yang mampu membuat hal rumit menjadi mudah, dan yang mampu bekerja sama seperti tim yang solid—bahkan jika tim itu terdiri dari jutaan “petugas surat” digital yang bekerja tanpa henti di balik layar.&lt;/p&gt;

</description>
      <category>go</category>
      <category>goroutine</category>
      <category>eventdriven</category>
      <category>backenddevelopment</category>
    </item>
  </channel>
</rss>
