Go'da context ile iptal ve zaman aşımı
Test edildiği sürüm: Go 1.26
İçindekiler
Bir HTTP isteği iptal edildiğinde, o istek için başlatılan veritabanı sorgusunun ve dış servis çağrılarının da durması gerekir. Go’da bunu sağlayan mekanizma context.Context.
Context’i her çağrıya aktarın
En sık gördüğüm hata, handler içinde context.Background() kullanmak:
func (h *Handler) GetUser(w http.ResponseWriter, r *http.Request) { // Yanlış: istemci bağlantıyı kapatsa bile sorgu devam eder. user, err := h.repo.Find(context.Background(), r.PathValue("id")) // ...}Doğrusu isteğin kendi context’ini kullanmak:
user, err := h.repo.Find(r.Context(), r.PathValue("id"))Zaman aşımını çağıran taraf belirlesin
Dış servise giden her çağrının bir üst sınırı olmalı. Bu sınırı çağrının yapıldığı yerde tanımlamak, okuyan kişinin davranışı tek bakışta anlamasını sağlar.
ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)defer cancel()
resp, err := h.payments.Charge(ctx, req)if errors.Is(err, context.DeadlineExceeded) { http.Error(w, "ödeme servisi yanıt vermedi", http.StatusGatewayTimeout) return}defer cancel() satırını atlamak, zaman aşımı dolana kadar kaynakların serbest bırakılmamasına neden olur.
İpucu
go vet içindeki lostcancel kontrolü, çağrılmayan cancel fonksiyonlarını yakalar. CI’da çalıştırmaya değer.
Goroutine içinde ctx.Done() dinleyin
Arka planda çalışan bir goroutineGoroutine Go çalışma zamanının yönettiği, işletim sistemi thread’inden çok daha hafif eşzamanlı çalışma birimi. Tüm terimler →, context iptal edildiğinde çıkmalı:
for { select { case <-ctx.Done(): return ctx.Err() case msg := <-messages: process(msg) }}Aşağıdaki örneği çalıştırıp zaman aşımının nasıl işlediğini görebilirsiniz:
package main
import ( "context" "fmt" "time")
func slowQuery(ctx context.Context) error { select { case <-time.After(2 * time.Second): return nil case <-ctx.Done(): return ctx.Err() }}
func main() { ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) defer cancel()
err := slowQuery(ctx) fmt.Println("sonuç:", err) // sonuç: context deadline exceeded}Sızıntıları yakalamak için testlerde goleak kullanmak iyi bir alışkanlık: