2026'da Go Context Paketi: İptal, Timeout ve Mülakat Soruları

Go context paketi hakkında kapsamlı rehber - Context arayüzünden WithCancel ve WithTimeout'a, ileri düzey kalıplara ve mülakat sorularına kadar her şey.

Go Context Paketi - iptal, timeout ve en iyi uygulamalar

Go context paketi, API sınırları arasında süre sınırları, iptal sinyalleri ve istek kapsamlı değerleri taşımak için standart mekanizmayı sağlar. Her üretim Go uygulaması context kullanır: HTTP handler'ları, veritabanı sorguları, gRPC çağrıları ve arka plan worker'ları düzgün kapatma ve timeout yönetimi için buna bağımlıdır.

Mülakat İçin Kritik

Context, Go mülakatlarında en sık sorulan konulardan biridir. Mülakatçılar adayların context yayılımını açıklamasını, doğru iptal işleme göstermesini ve context'i struct'larda saklama gibi yaygın hatalardan kaçınmasını bekler.

Context Arayüzü ve Dört Metodu

Context arayüzü, her context implementasyonunun karşılaması gereken dört metod tanımlar. Bu metodları anlamak, etkili context kullanımının temelini oluşturur.

context_interface.gogo
type Context interface {
    // Deadline returns the time when work should be canceled
    Deadline() (deadline time.Time, ok bool)
    
    // Done returns a channel that closes when the context is canceled
    Done() <-chan struct{}
    
    // Err returns the reason why Done was closed
    Err() error
    
    // Value returns the value associated with key, or nil
    Value(key any) any
}

Done() sadece okuma amaçlı bir kanal döndürür. Bu kanal kapandığında, onu dinleyen herhangi bir goroutine sinyali anında alır. <-ctx.Done() kalıbı iptal gerçekleşene kadar bloke eder. Err() nedenini açıklar: açıkça iptal edildiğinde context.Canceled, timeout veya son tarih geçtiğinde context.DeadlineExceeded.

Background ve TODO ile Context Oluşturma

İki fonksiyon kök context'ler oluşturur: context.Background() ve context.TODO(). Her ikisi de hiçbir zaman iptal edilmeyen boş olmayan context'ler döndürür.

context_creation.gogo
package main

import (
    "context"
    "log"
    "net/http"
)

func main() {
    // Background: the root context for your application
    ctx := context.Background()
    
    // Use it as parent for derived contexts
    server := &http.Server{Addr: ":8080"}
    
    // TODO: placeholder when context source is unclear
    // Static analysis tools can flag context.TODO() for review
    processLegacyData(context.TODO())
}

func processLegacyData(ctx context.Context) {
    // Context parameter enables future cancellation support
    log.Println("Processing data...")
}

Background() gelen istekler, main fonksiyonları ve başlatma kodu için üst eleman olarak hizmet eder. TODO() doğru context kaynağı henüz belirlenmediğinde refactoring sırasında placeholder görevi görür. Statik analiz araçları TODO() kullanımını inceleme için işaretleyebilir.

WithCancel ve WithCancelCause ile İptal

Manuel iptal, üst goroutine'lerin alt goroutine'lere işin durdurulması gerektiğini sinyal etmesine olanak tanır. Bu kalıp worker havuzlarında, arka plan görevlerinde ve graceful shutdown implementasyonlarında görülür.

cancellation.gogo
package main

import (
    "context"
    "errors"
    "fmt"
    "time"
)

func worker(ctx context.Context, id int, results chan<- int) {
    for {
        select {
        case <-ctx.Done():
            // Check why cancellation occurred
            if cause := context.Cause(ctx); cause != nil {
                fmt.Printf("Worker %d stopped: %v\n", id, cause)
            }
            return
        default:
            // Simulate work
            time.Sleep(100 * time.Millisecond)
            results <- id * 10
        }
    }
}

func main() {
    // WithCancelCause provides error context
    ctx, cancel := context.WithCancelCause(context.Background())
    results := make(chan int, 10)
    
    // Start 3 workers
    for i := 1; i <= 3; i++ {
        go worker(ctx, i, results)
    }
    
    // Collect some results
    for i := 0; i < 5; i++ {
        fmt.Println("Result:", <-results)
    }
    
    // Cancel with a specific reason
    cancel(errors.New("shutdown requested by user"))
    
    // context.Cause retrieves the cancellation reason
    time.Sleep(50 * time.Millisecond)
    fmt.Println("Cause:", context.Cause(ctx))
}

Go 1.20'de tanıtılan WithCancelCause, düz WithCancel'dan daha zengin hata bilgisi sağlar. context.Cause() fonksiyonu cancel fonksiyonuna geçirilen hatayı alır ve karmaşık sistemlerde hata ayıklamayı kolaylaştırır.

Go mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

İstek İşleme için Timeout ve Son Tarihler

HTTP handler'ları, veritabanı çağrıları ve harici API istekleri her zaman timeout'lara sahip olmalıdır. Context paketi bu amaçla WithTimeout ve WithDeadline sunar. Her ikisi de süre dolduğunda otomatik olarak iptal olan context'ler oluşturur.

timeouts.gogo
package main

import (
    "context"
    "fmt"
    "time"
)

// fetchFromAPI simulates an HTTP call
func fetchFromAPI(ctx context.Context, endpoint string) (string, error) {
    // Simulate variable response time
    responseTime := time.Duration(100+endpoint[0]%150) * time.Millisecond
    
    select {
    case <-time.After(responseTime):
        return fmt.Sprintf("Response from %s", endpoint), nil
    case <-ctx.Done():
        return "", ctx.Err()
    }
}

func main() {
    // WithTimeout: relative duration
    ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
    defer cancel() // Always call cancel to release resources
    
    result, err := fetchFromAPI(ctx, "api.example.com/users")
    if err != nil {
        if err == context.DeadlineExceeded {
            fmt.Println("Request timed out")
        } else {
            fmt.Println("Request canceled")
        }
        return
    }
    fmt.Println(result)
    
    // WithDeadline: absolute time
    deadline := time.Now().Add(500 * time.Millisecond)
    ctx2, cancel2 := context.WithDeadline(context.Background(), deadline)
    defer cancel2()
    
    result2, _ := fetchFromAPI(ctx2, "api.example.com/orders")
    fmt.Println(result2)
}

defer cancel() kalıbı, operasyon timeout'tan önce tamamlansa bile kaynakların serbest bırakılmasını sağlar. Bu çağrının atlanması kaynak sızıntısına neden olur: context'in timer goroutine'i timeout süresi dolana kadar aktif kalır.

WithValue ile İstek Kapsamlı Değerler

WithValue istek kapsamlı verileri context'e ekler. Yaygın kullanım durumları arasında istek ID'leri, kimlik doğrulama token'ları ve izleme span'ları bulunur. Resmi dokümantasyon çakışmaları önlemek için özel türlerin anahtar olarak kullanılmasını vurgular.

context_values.gogo
package main

import (
    "context"
    "fmt"
    "log"
    "net/http"
)

// Define custom key types to avoid collisions
type contextKey string

const (
    requestIDKey contextKey = "requestID"
    userIDKey    contextKey = "userID"
)

// middleware adds request ID to context
func requestIDMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        requestID := r.Header.Get("X-Request-ID")
        if requestID == "" {
            requestID = generateRequestID()
        }
        
        // Create new context with request ID
        ctx := context.WithValue(r.Context(), requestIDKey, requestID)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

// handler retrieves values from context
func handler(w http.ResponseWriter, r *http.Request) {
    requestID, ok := r.Context().Value(requestIDKey).(string)
    if !ok {
        requestID = "unknown"
    }
    
    log.Printf("[%s] Processing request", requestID)
    fmt.Fprintf(w, "Request ID: %s", requestID)
}

func generateRequestID() string {
    return fmt.Sprintf("req-%d", time.Now().UnixNano())
}

import "time"

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", handler)
    
    http.ListenAndServe(":8080", requestIDMiddleware(mux))
}

Context değerleri değişmez bir zincir oluşturur: her WithValue çağrısı üst elemanı saran yeni bir context oluşturur. Aramalar bu zinciri dolaşır, bu da derin iç içe değerlere erişimi yavaşlatır. Yalnızca istek kapsamlı verileri depolanmalı, uygulama yapılandırması veya opsiyonel parametreler değil.

WithoutCancel ve AfterFunc Yardımcı Araçları

Go 1.21, temizlik görevleri gibi üst iptalden bağımsız olarak tamamlanması gereken operasyonlar için WithoutCancel ekledi. AfterFunc context iptali gerçekleştiğinde çalışacak bir callback planlar.

utilities.gogo
package main

import (
    "context"
    "fmt"
    "log"
    "time"
)

func saveAuditLog(ctx context.Context, message string) error {
    // Use WithoutCancel: audit logs must complete even if request canceled
    cleanCtx := context.WithoutCancel(ctx)
    
    // Simulate database write
    select {
    case <-time.After(50 * time.Millisecond):
        log.Printf("Audit: %s", message)
        return nil
    case <-cleanCtx.Done():
        // This branch never executes: WithoutCancel context is never canceled
        return cleanCtx.Err()
    }
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
    
    // AfterFunc: register cleanup when context ends
    stop := context.AfterFunc(ctx, func() {
        fmt.Println("Context ended, running cleanup")
    })
    
    // Perform operation
    time.Sleep(50 * time.Millisecond)
    
    // Cancel stops the AfterFunc from running if called before context ends
    if stop() {
        fmt.Println("Cleanup prevented")
    }
    
    cancel()
    
    // Audit log completes despite parent cancellation
    saveAuditLog(ctx, "Operation completed")
}

WithoutCancel üst context'ten değerleri korur ancak iptal sinyalini yok sayar. Bu kalıp loglama, metrik yayınlama ve istek sonucundan bağımsız olarak başarılı olması gereken veritabanı commit'leri için uygundur.

Mülakat Soruları ve Cevapları

Go teknik mülakatına hazırlanmak, context iç yapısını ve en iyi uygulamaları anlamayı gerektirir. Bu sorular backend servisleri için Go kullanan şirketlerde sıkça karşılaşılır. Daha fazla Go mülakat hazırlığı için Context Paketi mülakat soruları modülüne bakılabilir.

Context neden ilk parametre olmalı?

Go ekibi context yayılımını görünür ve tutarlı kılmak için bu konvansiyonu oluşturdu. Context'i ilk sıraya koymak okuyuculara fonksiyonun iptali dikkate aldığını sinyal eder. Standart kütüphane bu kalıbı takip eder: http.Request.Context(), database/sql.QueryContext() ve grpc.UnaryInterceptor hepsi context'i ilk argüman olarak bekler.

Context'i struct'ta saklarsanız ne olur?

Context'i saklamak istek yaşam döngüsü modelini bozar. Bir struct için oluşturulduğu istekten daha uzun yaşayabilir ve operasyonların eski iptal sinyalleri kullanmasına veya yeni son tarihleri kaçırmasına neden olabilir. Context dokümantasyonu bu kalıba karşı açıkça uyarır.

go
// BAD: context stored in struct
type Service struct {
    ctx context.Context // Never do this
}

// GOOD: pass context to each method
type Service struct{}

func (s *Service) Process(ctx context.Context, data []byte) error {
    // Context flows through the call chain
    return s.save(ctx, data)
}

Goroutine'lerde context nasıl işlenir?

Goroutine'ler ana döngülerinde veya select ifadelerinde ctx.Done() kontrol etmelidir. Done kanalını yok saymak goroutine sızıntısı oluşturur: üst fonksiyon döner ancak goroutine kaynakları tüketmeye devam eder.

go
// Correct pattern for long-running goroutines
func processStream(ctx context.Context, stream <-chan Data) {
    for {
        select {
        case <-ctx.Done():
            log.Println("Shutting down processor")
            return
        case data, ok := <-stream:
            if !ok {
                return
            }
            handle(data)
        }
    }
}

context.TODO() ne zaman kullanılmalı?

Eski koda context desteği eklerken artımlı refactoring sırasında TODO() kullanılmalıdır. Uygun context yayılımı gereken yerleri işaretler. Üretim kodu sonunda tüm TODO() çağrılarını istek handler'larından veya uygulama başlatmasından gelen gerçek context'lerle değiştirmelidir. Go eşzamanlılık kalıpları hakkında daha fazla bilgi için Go Concurrency: Goroutines and Channels makalesine bakılabilir.

Kaçınılması Gereken Yaygın Hatalar

Context'in yanlış kullanımı yük altında veya kapatma sekansları sırasında ortaya çıkan ince hatalara yol açar. Bu kalıpları tanımak üretim olaylarını önler.

mistakes.gogo
package main

import (
    "context"
    "time"
)

// MISTAKE 1: Ignoring context cancellation
func badWorker(ctx context.Context) {
    for {
        // Missing select on ctx.Done()
        doExpensiveWork() // Never stops when context canceled
    }
}

// MISTAKE 2: Not calling cancel
func leakyTimeout() {
    ctx, _ := context.WithTimeout(context.Background(), time.Second)
    // Timer goroutine leaks until timeout expires
    _ = ctx
}

// MISTAKE 3: Using string keys for values
func collisionProne(ctx context.Context) context.Context {
    // Different packages might use same string key
    return context.WithValue(ctx, "userID", 123) // Bad
}

// CORRECT versions
func goodWorker(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            return
        default:
            doExpensiveWork()
        }
    }
}

func properTimeout() {
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel() // Always release resources
    _ = ctx
}

type userIDKey struct{}

func collisionSafe(ctx context.Context) context.Context {
    return context.WithValue(ctx, userIDKey{}, 123) // Good
}

func doExpensiveWork() {}

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Üretimde Go Context Kullanımı için Temel Çıkarımlar

  • Context fonksiyonlara ilk parametre olarak geçirilmeli, asla struct'larda saklanmamalıdır
  • Kaynak sızıntısını önlemek için WithTimeout, WithDeadline veya WithCancel sonrası her zaman defer cancel() çağrılmalıdır
  • Karmaşık iptal zincirlerinde daha iyi hata ayıklama için WithCancelCause ve context.Cause() kullanılmalıdır
  • Graceful shutdown sağlamak ve sızıntıları önlemek için goroutine döngülerinde <-ctx.Done() kontrol edilmelidir
  • Paketler arasında çakışmaları önlemek için WithValue için özel anahtar türleri kullanılmalıdır
  • Üst iptalden bağımsız olarak tamamlanması gereken temizlik operasyonları için WithoutCancel uygulanmalıdır
  • Uygulama başlatması için context.Background() tercih edilmeli, context.TODO() yalnızca refactoring sırasında kullanılmalıdır
Günün meydan okuması

Go kodundaki hatayı bulabilir misin?

Gerçek bir kod parçası, gizli bir hata, günde bir deneme. Denemek için hesap gerekmez.

Anthony Fillion-Maillet

Yazan:

Anthony Fillion-Maillet

SharpSkill kurucusu

10 yılı aşkın süredir fullstack geliştirici. SharpSkill’i yönetiyor ve burada yayımlanan her şeyden sorumlu.

29 Ağustos 2026 tarihinde güncellendi

Etiketler

#go
#context
#concurrency
#best-practices
#interview

Paylaş

İlgili makaleler