Menghadapi Goroutine Leak Go Fiber Streaming WebSocket berarti segera menemukan penyebabnya karena gejalanya langsung terlihat lewat latensi naik dan goroutine count yang terus membengkak. Artikel ini langsung menyoroti cara menganalisis kebocoran tersebut menggunakan pprof dan log konteks, lalu menyarankan perubahan handler agar context cancellable dan loop I/O berhenti saat klien putus koneksi.
Gejala awal dan indikasi kebocoran goroutine
Setelah deployment, endpoint WebSocket yang seharusnya streaming data real-time mulai menurun responsivitasnya. Gejala yang terlihat:
- Latensi merangkak naik secara gradual, terutama saat jumlah koneksi aktif bertambah.
- Goroutine count di runtime/go:cpu atau metric Prometheus tumbuh terus tanpa turun.
- Heap dan stack profile menunjukkan goroutine terbuka dalam jumlah besar yang menunggu read/write.
Kasus ini bukan hanya soal resource leak, tapi juga dampak langsung terhadap pengalaman pengguna streaming.
Diagnosa Goroutine Leak Go Fiber Streaming WebSocket
Langkah diagnosis fokus pada dua alat sehari-hari: pprof dan log berbasis context. Pertama, ambil profile goroutine saat load sedang tinggi:
go tool pprof http://localhost:6060/debug/pprof/goroutine
Profil menampilkan goroutine stuck dalam loop read atau write yang tidak pernah keluar. Kombinasi ini menunjukkan loop tidak menerima sinyal pembatalan saat klien disconnect.
Log context membantu memastikan apakah request context diteruskan ke goroutine pembaca/penulis. Tanpa log ini, kita sering melewatkan bahwa ctx.Done() tidak pernah diperiksa.
Analisis akar masalah: context tidak diteruskan dan deadlock
Penyebab utama kebocoran adalah handler yang memulai goroutine baru untuk read/write streaming tanpa meneruskan context.Context. Ketika klien disconnect, Fiber membatalkan context, namun goroutine streaming tetap menunggu I/O karena tidak memeriksa ctx.Done(). Skenario ini menciptakan deadlock internal karena goroutine menunggu operasi WebSocket yang tidak akan pernah selesai.
Contoh sederhana handler bermasalah:
func streamingHandler(c *fiber.Ctx) error {
websocketConn, err := websocket.Accept(c.Context(), c.Context().Request(), nil)
if err != nil {
return err
}
go func() {
for {
msg, err := websocketConn.ReadMessage(c.Context())
if err != nil {
return
}
websocketConn.WriteMessage(c.Context(), msg)
}
}()
<-c.Context().Done()
return nil
}
Di atas, goroutine tidak pernah melihat ctx.Done() langsung, sehingga tetap berjalan meskipun permintaan telah selesai.
Perbaikan: propagasi context dan penanganan error
Solusi praktis meliputi:
- Propagasi context: pastikan goroutine menerima context cancellable dan memeriksa
ctx.Done()di loop. - Defer cancel: gunakan
context.WithCanceluntuk mengontrol lifecycle goroutine secara eksplisit. - Review error handling: tangani kesalahan read/write dengan benar sehingga goroutine bisa keluar tanpa menunggu sinyal lain.
- Monitoring: pantau metric goroutine count dan latency per endpoint sebagai early warning.
Implementasi yang lebih sehat:
func streamingHandler(c *fiber.Ctx) error {
ctx, cancel := context.WithCancel(c.Context())
defer cancel()
websocketConn, err := websocket.Accept(ctx, c.Context().Request(), nil)
if err != nil {
return err
}
errCh := make(chan error, 1)
go func() {
defer close(errCh)
for {
select {
case <-ctx.Done():
return
default:
}
msgType, msg, err := websocketConn.ReadMessage(ctx)
if err != nil {
errCh <- err
return
}
if err := websocketConn.WriteMessage(ctx, msgType, msg); err != nil {
errCh <- err
return
}
}
}()
select {
case <-ctx.Done():
return ctx.Err()
case err := <-errCh:
return err
}
}
Loop membaca dan menulis secara eksplisit memeriksa ctx.Done(). Ketika klien disconnect, ctx dibatalkan dan goroutine keluar, sehingga goroutine count turun kembali.
Pencegahan regresi dan takeaways praktis
Untuk menghindari kasus serupa:
- Pastikan setiap goroutine yang berhubungan dengan request menerima context dan mengecek
Done. - Gunakan profiling goroutine secara berkala setelah deployment untuk mendeteksi anomali jumlah goroutine.
- Tambahkan observabilitas dengan log kontekstual yang mencakup request ID dan status loop.
- Terapkan review error handling untuk semua jalur read/write: kegagalan I/O harus memicu keluar loop, bukan diam.
Dengan pendekatan ini, Goroutine Leak Go Fiber Streaming WebSocket bukan hanya dideteksi lebih cepat, tapi juga dilokalisasi dan diperbaiki secara sistematis.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!