Belajar Chi - Performance & Troubleshooting
Series/Belajar Chi/Episode 19
Episode 19 of 23

Belajar Chi - Performance & Troubleshooting

Episode ini memoles performa: pola rute yang efisien, tuning http.Server, connection pooling, dan caching dengan Redis. Kalian juga belajar memecahkan masalah umum seperti route pattern conflict, panic handler, deadlock context, kebocoran goroutine, serta strategi upgrade antar versi chi.

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

Pendahuluan

Aplikasi yang sudah benar belum tentu cepat. Episode 19 menutup sisi teknis: cara memompa performa tanpa mengorbankan kebenaran, lalu cara memecahkan masalah klasik yang menghantui setiap project Go production.

Bagian pertama membahas optimasi: pola rute, tuning server, pooling, dan caching. Bagian kedua membahas troubleshooting: gejala, akar masalah, dan solusi untuk lima masalah paling umum di ekosistem chi.

Pola Rute yang Efisien

Struktur yang Membantu Router

radix tree chi bekerja optimal dengan pola yang teratur:

Pola rute efisien
r.Get("/users", listUsers)
r.Get("/users/{id:[0-9]+}", getUser)
r.Post("/users", createUser)

{id:[0-9]+} lebih efisien daripada param bebas untuk identifier numerik — regex lebih spesifik, lebih sedikit kandidat yang dicoba. Hindari wildcard {path:*} di rute yang sering dipanggil kecuali memang diperlukan.

Hindari Ambigu

Dua rute yang bisa cocok dengan request yang sama memperlambat matching dan membingungkan. Buat pola se-spesifik mungkin dan gunakan subrouter untuk mengelompokkan path yang berbagi prefix — radix tree akan berbagi node secara otomatis.

Tuning http.Server

Timeout dan Batas Header

Konfigurasi server memberi perlindungan beban:

Tuning http.Server
srv := &http.Server{
    Addr:         ":8080",
    Handler:      r,
    ReadTimeout:  5 * time.Second,
    WriteTimeout: 10 * time.Second,
    IdleTimeout:  120 * time.Second,
    MaxHeaderBytes: 1 << 20,
}

ReadTimeout membatasi waktu membaca request; WriteTimeout membatasi penulisan respons. MaxHeaderBytes mencegah header raksasa. Nilai ini menurunkan peluang serangan lambat dan koneksi menggantung.

Kenapa Timeout Penting

Tanpa timeout, koneksi yang lambat bisa menahan goroutine selama-lamanya — setiap koneksi memakan goroutine, dan goroutine yang menggantung menguras memory. Nilai di atas adalah titik awal yang wajar untuk API JSON.

Connection Pooling

Atur Pool Database

Pool yang sudah dipakai sejak episode 9 perlu di-set untuk beban nyata:

Konfigurasi pool
pool.Config().MaxConns = 20
pool.Config().MinConns = 5
pool.Config().MaxConnLifetime = time.Hour
pool.Config().MaxConnIdleTime = 5 * time.Minute

MaxConns membatasi koneksi paralel maksimal ke database; MinConns menjaga koneksi siap pakai. MaxConnLifetime memaksa rotasi koneksi agar tidak memakai koneksi basi. Ukuran yang pas bergantung pada spesifikasi database.

Caching dengan Redis

Cache untuk Data Panas

Respons yang sering diminta jangan dihitung ulang:

Install client Redis
go get github.com/redis/go-redis/v9
Cache dengan Redis
client := redis.NewClient(&redis.Options{
    Addr: "localhost:6379",
})
 
func cacheOrFetch(w http.ResponseWriter, req *http.Request) {
    ctx := req.Context()
 
    val, err := client.Get(ctx, "popular").Result()
    if err == nil {
        writeJSON(w, http.StatusOK, val)
        return
    }
 
    data := fetchSlowData(ctx)
    client.Set(ctx, "popular", data, time.Minute)
    writeJSON(w, http.StatusOK, data)
}

client.Get(ctx, "popular") membaca cache lebih dulu; bila kosong, data dihitung lalu disimpan dengan client.Set(ctx, "popular", data, time.Minute). TTL satu menit memastikan cache tidak basi selamanya.

Kapan Cache Tidak Tepat

Jangan cache data per-user yang sensitif di key global, dan jangan cache data yang berubah cepat tanpa invalidasi. Cache terbaik adalah untuk data yang jarang berubah dan mahal dihitung.

Troubleshooting Umum

Route Pattern Conflict

Gejala: rute didaftarkan tapi tidak pernah cocok, atau panic saat startup. Solusinya: radix tree chi melarang dua pola ambigu pada posisi yang sama. Periksa urutan — literal harus menang atas param, dan jangan daftarkan dua wildcard di segmen yang sama.

Panic yang Tidak Tertangkap

Gejala: server mati mendadak tanpa log. Solusi: pasang middleware.Recoverer paling atas r.Use:

Recoverer sebagai pengaman
r.Use(middleware.RequestID)
r.Use(middleware.Recoverer)

middleware.Recoverer mencetak stack trace ke log — bahan utama untuk menemukan baris yang panic.

Deadlock Context

Gejala: handler menggantung, timeout tak pernah jalan. Penyebab: context tidak diteruskan, atau channel menunggu tanpa select dengan ctx.Done(). Solusi: selalu seleksi dengan <-ctx.Done() (episode 12) dan teruskan req.Context() ke setiap operasi.

Kebocoran Goroutine

Gejala: memory naik terus meski beban stabil. Penyebab: goroutine tanpa jalan keluar, misal go func() yang menulis ke unbuffered channel tanpa pembaca. Solusi:

Cegah kebocoran goroutine
go func() {
    select {
    case ch <- result:
    case <-ctx.Done():
    }
}()

select { case ch <- result: case <-ctx.Done(): } memberi goroutine jalan keluar saat request dibatalkan. Pantau dengan pprof go tool pprof untuk menemukan goroutine yang menggantung.

Upgrade Antar Versi chi

Gejala: kode lama rusak setelah go get versi baru. Solusinya: baca release notes, jalankan test dulu, dan perbarui bertahap:

Upgrade chi
go get github.com/go-chi/chi/v5@latest
go mod tidy
go test ./...

go get github.com/go-chi/chi/v5@latest lalu go test ./... memastikan upgrade tidak merusak perilaku. Detail fitur per versi kita bahas di episode 20.

Penutup

Inti yang harus dibawa pulang:

  • Regex spesifik dan subrouter membantu radix tree mencocokkan cepat.
  • ReadTimeout dan WriteTimeout melindungi dari request lambat.
  • Atur pool: MaxConns, MinConns, dan MaxConnLifetime.
  • Redis caching memangkas latensi untuk data yang jarang berubah.
  • Conflict rute dicegah dengan pola yang tidak ambigu.
  • Recoverer, ctx.Done, dan pprof memecahkan panic, deadlock, dan leak.

Di episode 20 selanjutnya kita melihat yang terbaru: fitur stabil terbaru chi v5.2 dan v5.3 — dukungan empat versi Go terakhir, middleware.Discard dan perbaikan RedirectSlashes di v5.2, serta inline middleware di subrouter, dukungan XML di default compressible types, penggantian io/ioutil, dan CI Go 1.26 di v5.3.

Belajar Chi - Performance & Troubleshooting | Belajar Chi