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
2dan3tetap muncul tiap 25 detik, tapi gak ada42["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
2probe,3probe,5: Engine.IO menguji lalu pindah ke WebSocket (pelajaran Socket.IO).420[...]event dengan ack nomor 0,430[...]jawabannya.42[...]event tanpa ack.- Setiap 25 detik server mengirim
2(ping) dan client membalas3(pong). Kalau cuma itu yang lewat, koneksinya sehat tapi gak ada yang dikirim ke socket ini.
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
- Koneksi sekarang:
io.engine.clientsCount. Turun separuh dalam satu menit hampir pasti masalah server atau proxy. - Laju reconnect dan alasan putus: hitung
disconnectper alasan (transport close,ping timeout) danconnect_errorper pesan. - Lag pesan: selisih waktu server mengirim dan client menerima. Jam HP bisa meleset menit-menitan, jadi ukur dengan ack pulang-pergi atau cek urutan dengan
seq, jangan membandingkan jam dua perangkat.
⚠️ Jebakan yang sering ketemu
- Menguji dengan satu tab dan satu proses. Bug room, adapter, dan sticky cuma muncul dengan beberapa client dan beberapa proses.
- Log tanpa nomor. Tanpa
cidatauseq, log tablet, server, dan HP gak bisa dicocokkan. - Mengira
localStorage.debugrusak. Build bundler-nya memang gak membawadebug. DEBUG=*dibiarkan di produksi. Log membanjir, dan payload (bisa berisi data pelanggan) ikut tercatat.
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.