Belajar WebSocket - Message Queuing & Reliability
Episode 14 of 34

Belajar WebSocket - Message Queuing & Reliability

Episode ini membahas keandalan pengiriman pesan: jaminan at-most-once hingga exactly-once, mekanisme acknowledgement, antrean pesan offline, persistence ke database dan message broker, serta menjaga urutan pesan.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

WebSocket hanya menjamin pesan terkirim ke jaringan, bukan sampai ke tujuan. Jika klien offline saat pesan dikirim, atau server crash setelah menerima, pesan itu hilang begitu saja. Untuk aplikasi kritikal seperti transfer dana atau order, kehilangan itu tidak bisa diterima.

Episode 14 membahas reliability: jaminan pesan tidak hilang atau terduplikasi, dan queuing: menyimpan pesan saat penerima belum siap. Kalian akan mempelajari model jaminan pengiriman, acknowledgement, antrean offline, dan persistence.

Jaminan Pengiriman

Tiga Level Jaminan

Sistem pesan umumnya menawarkan tiga jaminan dengan biaya yang berbeda.

Level jaminan pengiriman
at-most-once   : pesan mungkin hilang, tidak pernah duplikat
at-least-once  : pesan pasti sampai, mungkin duplikat
exactly-once   : pasti sampai dan tidak duplikat (paling mahal)

Default WebSocket adalah at-most-once: pesan dikirim sekali, jika gagal maka hilang. Untuk jaminan lebih tinggi, aplikasi harus menambahkan acknowledgement dan retry di atas protokol.

Trade-Off Setiap Level

  • At-most-once: cepat dan ringan, cocok untuk update posisi mouse yang boleh hilang.
  • At-least-once: retry sampai ada ack, tapi bisa menghasilkan duplikat yang harus di-deduplikasi.
  • Exactly-once: membutuhkan deduplikasi berbasis ID dan persistence, mahal secara kompleksitas.

Pilih level yang sesuai dengan konsekuensi kehilangan pesan di aplikasi kalian.

Acknowledgement

Ack dan Timeout

Klien memberi tahu server bahwa pesan sudah diterima dengan mengirim balasan ack.

JSAck dengan ID pesan di server
wss.on("connection", (ws) => {
  ws.on("message", (data) => {
    const pesan = JSON.parse(data.toString());
 
    simpanPesan(pesan.id, pesan);
 
    ws.send(JSON.stringify({
      type: "ack",
      id: pesan.id,
    }));
  });
});

simpanPesan(pesan.id, pesan) menyimpan pesan sebelum ack dikirim. Jika server crash setelah ack, pesan sudah tersimpan; jika sebelum ack, klien akan mengirim ulang dan server mendeteksi duplikat lewat ID.

Retry dengan Deduplikasi

Sisi pengirim harus mencoba ulang saat ack tidak datang, dan penerima harus menolak duplikat.

JSKirim ulang sampai di-ack
let idBerikutnya = 1;
const terack = new Set();
 
function kirimDenganAck(ws, pesan) {
  const id = idBerikutnya++;
  pesan.id = id;
 
  ws.send(JSON.stringify(pesan));
 
  setTimeout(() => {
    if (!terack.has(id)) {
      kirimDenganAck(ws, Object.assign({}, pesan));
    }
  }, 3000);
}

terack.has(id) menandai pesan yang sudah dikonfirmasi penerima. Timer 3 detik memicu pengiriman ulang sampai ack tiba, dengan batas percobaan untuk mencegah pengiriman tanpa henti.

Message Queuing

Antrean Offline

Saat penerima offline, pesan harus disimpan sampai dia kembali.

JSQueue pesan offline
const antreanOffline = new Map();
 
socket.on("message:send", (pesan) => {
  const tujuan = penggunaOnline(pesan.to);
 
  if (tujuan) {
    tujuan.emit("message:new", pesan);
  } else {
    const q = antreanOffline.get(pesan.to) || [];
    q.push(pesan);
    antreanOffline.set(pesan.to, q);
  }
});
 
socket.on("presence:online", (userId) => {
  const q = antreanOffline.get(userId) || [];
  q.forEach((pesan) => socket.emit("message:new", pesan));
  antreanOffline.delete(userId);
});

antreanOffline menyimpan pesan per pengguna. Saat pengguna kembali online, seluruh antrean dikirim dan dibersihkan.

Priority Queue

Tidak semua pesan sama pentingnya. Antrean prioritas mengirim pesan penting lebih dulu.

JSAntrean dengan prioritas
const antrean = [];
 
function tambah(pesan, prioritas) {
  antrean.push({ pesan, prioritas });
  antrean.sort((a, b) => b.prioritas - a.prioritas);
}

antrean.sort(...) menjaga urutan berdasarkan prioritas. Peringatan sistem selalu diambil sebelum notifikasi promo.

Persistence

Database dan Message Broker

JSSimpan pesan ke database
const tersimpan = await db.pesan.create({
  data: {
    id: pesan.id,
    dari: pesan.dari,
    ke: pesan.ke,
    teks: pesan.teks,
  },
});

Penyimpanan di database membuat pesan bertahan melewati restart. Untuk throughput tinggi, message broker seperti Redis atau RabbitMQ menangani antrean dengan jaminan pengiriman yang sudah teruji. Struktur list dan pola Pub/Sub Redis akan dibahas lebih dalam di episode 16.

Menjaga Urutan Pesan

Sequence Number

Jaringan tidak menjamin pesan sampai berurutan. Tambahkan nomor urut agar penerima bisa mendeteksi pesan yang tertukar atau hilang.

JSDeteksi pesan hilang
let diharapkan = 1;
 
function proses(pesan) {
  if (pesan.seq !== diharapkan) {
    socket.emit("sync:request", { mulai: diharapkan });
    return;
  }
  tampilkan(pesan);
  diharapkan = pesan.seq + 1;
}

pesan.seq !== diharapkan menandakan ada pesan yang terlewat atau di luar urutan. Pola ini dikenal sebagai gap detection: klien meminta sinkronisasi ulang hanya dari nomor yang hilang, lalu melanjutkan pemrosesan. Ini hemat bandwidth dibanding mengirim ulang seluruh history.

Penutup

Episode 14 melengkapi WebSocket dengan keandalan yang tidak diberikan protokol secara bawaan: jaminan pengiriman dipilih sesuai kebutuhan, ack mencegah kehilangan, antrean menahan pesan untuk penerima yang offline, dan persistence membuat pesan bertahan dari restart.

Inti yang harus dibawa pulang:

  • WebSocket default hanya at-most-once; jaminan lebih tinggi butuh kerja ekstra.
  • Acknowledgement dengan ID memungkinkan retry dan deduplikasi.
  • Pesan untuk pengguna offline diantrekan dan dikirim saat kembali online.
  • Antrean di memori hilang saat restart, jadi persisten ke database atau broker.
  • Sequence number mendeteksi pesan yang hilang atau tertukar urutan.
  • Gap detection menghemat bandwidth dengan meminta hanya bagian yang hilang.

Di episode 15 berikutnya kita masuk ke fase scaling: load balancing WebSocket connections — sticky sessions, load balancer Layer 4 dan Layer 7, serta health check untuk zero-downtime deployment.

Belajar WebSocket - Message Queuing & Reliability | Belajar WebSocket