Episode ini mengoptimasi dan memperbaiki aplikasi Echo: mengurangi alokasi dengan reuse, connection pooling, caching Redis, tuning HTTP server, serta troubleshooting route conflict, error binding, deadlock, kebocoran goroutine, dan migrasi v4 ke v5.

Performa bukan soal mengejar angka benchmark, tapi menghilangkan pemborosan. Episode ini menggabungkan dua sisi: optimasi yang terukur untuk mempercepat aplikasi, dan troubleshooting untuk menemukan akar masalah saat sesuatu berjalan lambat atau salah.
Episode 19 ini membahas pengurangan alokasi dan reuse, connection pooling, caching Redis, tuning HTTP server, serta troubleshooting route conflict, error binding, deadlock, kebocoran goroutine, dan migrasi v4 ke v5.
Jangan mengoptimasi berdasarkan tebakan. Gunakan benchmark dari episode 17 untuk menemukan titik pemborosan:
import _ "net/http/pprof"Lalu ambil CPU profile saat beban:
go test -bench=. -benchmem -cpuprofile=cpu.out ./internal/handler/
go tool pprof -top cpu.outgo tool pprof -top cpu.out menampilkan fungsi yang paling banyak menghabiskan CPU. Optimasi dimulai dari baris teratas daftar ini, bukan dari dugaan.
Pola umum pemborosan: membuat slice atau buffer baru setiap request. Gunakan sync.Pool untuk objek yang sering dipakai dan mahal dibuat:
var bufPool = sync.Pool{
New: func() any { return bytes.NewBuffer(make([]byte, 0, 4096)) },
}
func handler(c echo.Context) error {
buf := bufPool.Get().(*bytes.Buffer)
defer bufPool.Put(buf)
buf.Reset()
buf.WriteString("hasil proses yang membutuhkan buffer besar")
return c.Blob(http.StatusOK, echo.MIMETextPlain, buf.Bytes())
}sync.Pool menyimpan objek yang bisa dipakai ulang antar request, mengurangi alokasi dan beban GC secara signifikan.
Pooling bukan hanya untuk database. Klien HTTP keluar juga harus di-pool agar TCP connection dipakai ulang:
var httpClient = &http.Client{
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 20,
IdleConnTimeout: 90 * time.Second,
},
}Satu klien global yang di-pool jauh lebih cepat daripada membuat klien baru per request.
Redis memangkas latensi untuk data yang jarang berubah. Gunakan Redis sebagai cache di depan query:
import "github.com/redis/go-redis/v9"
func getProduct(ctx context.Context, id int) (*model.Product, error) {
key := "product:" + strconv.Itoa(id)
if cached, err := rdb.Get(ctx, key).Result(); err == nil {
var p model.Product
if json.Unmarshal([]byte(cached), &p) == nil {
return &p, nil
}
}
p, err := repo.FindByID(ctx, id)
if err != nil {
return nil, err
}
data, _ := json.Marshal(p)
rdb.Set(ctx, key, data, 10*time.Minute)
return p, nil
}Pola cache-aside: baca cache dulu, hit database jika kosong, lalu simpan hasilnya dengan TTL.
Episode 10 memperkenalkan http.Server custom. Beberapa nilai tuning tambahan untuk beban produksi:
s := &http.Server{
Addr: ":" + cfg.Port,
ReadHeaderTimeout: 10 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second,
MaxHeaderBytes: 1 << 20,
}ReadHeaderTimeout melindungi dari slow-loris, MaxHeaderBytes membatasi ukuran header. Setiap nilai dipilih berdasarkan perilaku klien, bukan angka acak.
Error saat mendaftarkan route yang duplikat: gunakan e.Routes() dan e.Debug = true (episode 4) untuk melihat route yang sudah terdaftar sebelum mendaftarkan yang baru.
Payload yang tidak cocok menghasilkan error binding. Periksa tag struct, Content-Type, dan ukuran body yang dibatasi BodyLimit. Error validasi biasanya membawa pesan field spesifik — tampilkan untuk debugging.
Deadlock sering berasal dari channel yang tidak pernah dikirim atau dikonsumsi. Kebocoran goroutine muncul saat context tidak dibatalkan — misalnya goroutine yang tidak pernah menerima sinyal cancel. Deteksi dengan pprof:
import _ "net/http/pprof"curl http://localhost:6060/debug/pprof/goroutine?debug=1/debug/pprof/goroutine menampilkan semua goroutine aktif beserta stack-nya. Goroutine yang menumpuk pada fungsi yang sama adalah petunjuk kebocoran.
Migrasi mengikuti dokumen resmi API_CHANGES_V5.md. Perubahan utama dijelaskan di episode 20; langkah praktisnya:
go get github.com/labstack/echo/v5@latest
go mod tidy
go build ./...Mulai dengan update import, lalu perbaiki satu per satu error kompilasi yang muncul.
Episode 19 menggabungkan optimasi dan perbaikan: benchmark dengan pprof memandu keputusan, sync.Pool mengurangi alokasi, pooling dan Redis memangkas latensi, tuning server menyesuaikan kapasitas, dan teknik troubleshooting menangani route conflict, binding error, deadlock, kebocoran goroutine, serta migrasi v4 ke v5.
Inti yang harus dibawa pulang:
sync.Pool mereuse objek dan mengurangi alokasi.API_CHANGES_V5.md secara bertahap.Di episode 20 selanjutnya kita akan membahas fitur stabil terbaru Echo v5 — *echo.Context sebagai pointer receiver, RequestLogger dengan slog, router concurrent yang lebih aman, perubahan API breaking, serta perbaikan CVE-2026-55677 dan kebijakan dukungan v4.