Debugging Real-time Systems — Realtime

Debugging Real-time Systems Real-time bugs sulit di-reproduce: timing-dependent, state-dependent, multi-client. Tools dan teknik khusus dibutuhkan. Browser…

Debugging sistem realtime artinya mencari kenapa sebuah kabar gak sampai, sampai dua kali, telat, atau salah urutan, padahal kejadiannya melibatkan beberapa perangkat, sebuah server, jaringan di antaranya, dan waktu. Bayangin melacak paket yang hilang di jasa kurir. Kamu gak menebak; kamu membuka catatan di setiap titik serah terima (dikirim, diterima gudang, dibawa kurir, sampai), dan setiap paket punya nomor resi supaya catatannya bisa dicocokkan.

🧪 Tebakan cepat: Wi-Fi laundry putus sedetik. HP Pak Darto tersambung lagi, tapi sejak itu gak pernah menerima kabar #12. Di tab Messages DevTools, frame 2 dan 3 tetap muncul tiap 25 detik, tapi gak ada 42["order:status",...]. Penyebab paling mungkin? Jawabannya ada di bagian "Daftar kegagalan yang sudah kita temui".

Lihat frame-nya di DevTools

Di Chrome: buka DevTools, tab Network, pilih filter Socket (di versi lama dan di sebagian dokumentasi namanya WS; sekarang satu tombol untuk WebSocket, WebTransport, dan DirectSocket), klik koneksinya, lalu tab Messages. Isinya 100 pesan terakhir: kiriman berwarna hijau muda, terimaan putih, bisa disaring dengan regex. Centang Preserve log supaya gak hilang saat halaman di-reload.

Ini frame yang benar-benar lewat di Chromium 147 saat halaman papan tersambung lalu Mas Eko mengirim status dengan ack, dibaca lewat DevTools Protocol (sumber data tab Messages):

kirim   2probe                                                  6 B
terima  3probe                                                  6 B
kirim   5                                                       1 B
kirim   420["order:status",{"id":12,"status":"Siap diambil","cid":"c-7f3a"}]   68 B
terima  42["order:status",{"id":12,"status":"Siap diambil","seq":4}]           60 B
terima  430[{"ok":true}]                                       16 B

Di Firefox: tab Network, filter WS, klik koneksi, tab Response. Firefox juga mengurai payload Socket.IO menjadi pohon. Untuk SSE: Chrome punya tab EventStream, Firefox menampilkannya di tab Response. Throttling di tab Network ikut memperlambat WebSocket, berguna untuk meniru HP di sinyal lemah.

Nyalakan log Socket.IO

Server:

$ DEBUG=socket.io:socket,socket.io:client node server.js
2026-10-11T03:51:06.114Z socket.io:socket join room order:12
2026-10-11T03:51:06.217Z socket.io:socket got packet {"type":2,"nsp":"/","id":0,"data":["order:status",{"id":12,"status":"Siap diambil","cid":"c-7f3a"}]}
2026-10-11T03:51:06.217Z socket.io:socket sending ack [{"ok":true}]
2026-10-11T03:51:07.616Z socket.io:client client close with reason transport close

DEBUG=socket.io* menyalakan semuanya, termasuk Engine.IO. Waktu ISO di depan muncul kalau output-nya diarahkan ke file; di terminal biasa tampil +0ms di belakang.

Browser: localStorage.debug = 'socket.io-client:socket', lalu reload. Dicoba: ini cuma bekerja dengan build yang membawa modul debug, yaitu /socket.io/socket.io.js yang disajikan server, atau import { io } from 'socket.io-client/debug'. Kalau kamu memakai bundler (Vite) dengan import { io } from 'socket.io-client', build yang dipakai adalah build/esm yang gak membawa debug, jadi gak ada yang tercetak. socket.io.min.js juga gak membawanya.

Log dengan nomor resi: cid dan seq

Log Socket.IO menunjukkan paket, tapi gak tahu alur aplikasimu. Tulis satu baris log terstruktur di setiap titik serah terima, dengan nomor yang sama dari ujung ke ujung: cid dari pelajaran Chat (dibuat pengirim) dan seq dari pelajaran Dashboard (dinaikkan server).

const log = (hop, data) => console.log(JSON.stringify({ t: new Date().toISOString(), hop, ...data }))

socket.onAny((event, ...args) => log('server.terima', { socket: socket.id, event, cid: args[0]?.cid }))
socket.onAnyOutgoing((event, ...args) => log('server.kirim', { socket: socket.id, event, seq: args[0]?.seq }))

// saat menyiarkan, catat juga berapa socket di room itu
const isi = io.sockets.adapter.rooms.get('order:12')?.size ?? 0
log('server.emit', { room: 'order:12', cid, seq, sockets: isi })

Lalu cari satu cid di log semua pihak. Tablet mencatat "dikirim", server mencatat "diterima" dan "disiarkan ke 1 socket", HP Pak Darto gak mencatat apa-apa: masalahnya di antara server dan HP, bukan di tablet.

Paksa race supaya bisa diulang

Bug timing muncul sekali dalam sepuluh percobaan. Tambahkan jeda buatan di satu titik supaya urutannya selalu sama, lalu uji perbaikannya di jeda itu:

// sementara, cuma di lingkungan dev: buat snapshot selalu lambat 300 ms
const snap = await new Promise((r) => setTimeout(r, 300)).then(() => fetch('/api/dashboard'))

Dengan jeda ini, race snapshot dan delta dari pelajaran Dashboard terjadi setiap kali, bukan kadang-kadang. Kalau perbaikannya lolos sepuluh kali berturut-turut dengan jeda 0, 300, dan 1.000 ms, baru percaya.

Daftar kegagalan yang sudah kita temui

Gejala Penyebab Lihat di mana
Tab ketujuh macet tanpa error SSE di HTTP/1.1: 6 koneksi per domain Network: request ketujuh pending. Pakai HTTP/2
Client di proses B gak dapat kabar Tanpa Redis adapter, atau B sedang restart (Pub/Sub gak menyimpan) Balasan PUBLISH: jumlah penerima
disconnect baru datang 20 sampai 45 detik kemudian Ping timeout: 25 detik interval + 20 detik timeout Alasan ping timeout di log server
Kabar diproses dua kali Kiriman ulang (retry) tanpa cek cid Dua baris log dengan cid sama
Gak ada kabar setelah reconnect Join room cuma sekali; socket baru gak ada di room Log DEBUG: join room order:12 gak muncul lagi
Event gak pernah ditangani, tanpa error Nama event beda: emit order:status, dengar order-status Frame-nya ada di Messages, handler-nya gak jalan
Halaman situs lain tersambung sebagai Bu Rini CSWSH: Origin gak dicek Header Origin di handshake
Koneksi diam putus di detik 60 proxy_read_timeout nginx Kode 1006 di event close

Itu jawaban tebakannya: room basi setelah reconnect. Koneksinya hidup (ping dan pong lewat), tapi socket baru belum join ke order:12. Di log server, baris join room order:12 gak muncul setelah reconnect. Perbaikannya: kirim ikuti di handler connect, yang jalan di setiap sambungan. Kalau pesan gak sampai ke satu client, cek dulu dua hal ini: sudah join room yang benar, dan nama event-nya sama persis di kedua sisi.

Angka yang diawasi

⚠️ Jebakan yang sering ketemu

TL;DR: Mulai dari frame: Chrome Network, filter Socket (dulu WS), tab Messages (100 pesan terakhir, regex, Preserve log); Firefox filter WS, tab Response; SSE di tab EventStream. Ping 2 dan pong 3 tiap 25 detik berarti koneksi sehat. Log server dengan DEBUG=socket.io*; di browser localStorage.debug cuma bekerja dengan /socket.io/socket.io.js atau socket.io-client/debug. Catat setiap titik serah terima dengan cid dan seq, paksa race dengan jeda buatan, dan cocokkan gejala dengan daftar: tab ketujuh SSE, adapter Redis, ping timeout 45 detik, kiriman dobel, room basi setelah reconnect, nama event beda, CSWSH, proxy 60 detik. Awasi jumlah koneksi, laju reconnect per alasan, dan lag pesan.

Yang akan kamu pelajari