Portal/Notes πŸ“
Golang

Context

Memahami package context di Go β€” cara mengelola deadline, sinyal cancelllation, dan data berbasis request.

Versi Bahasa Indonesia. Baca dalam Bahasa Inggris

Package context di Go menyediakan cara untuk mengelola data berbasis request, deadline, dan sinyal pembatalan (cancellation) melintasi batas API dan antar proses.

Ini wajib dipahami jika Anda ingin membangun aplikasi production-ready yang robust di Go, terutama untuk menangani workload konkuren seperti HTTP server, query database, dan task latar belakang.

Request masuk ke server harus membuat Context, dan panggilan keluar ke server lain harus menerima Context. Rantai fungsi di antaranya harus menyebarkan (propagate) Context, opsional menggantinya dengan Context turunan yang dibuat menggunakan WithCancel, WithDeadline, WithTimeout, atau WithValue. Ketika suatu Context dibatalkan, semua Context turunannya juga ikut dibatalkan.

Context

context.Context

Interface dasar yang menyediakan method untuk mengambil deadline, sinyal pembatalan, dan nilai.

  • Done() <-chan struct{}: Mengembalikan channel yang tertutup ketika context dibatalkan atau timeout.
  • Err() error: Mengembalikan alasan pembatalan, jika ada.
  • Deadline() (time.Time, bool): Mengembalikan deadline dan apakah deadline ditetapkan.
  • Value(key any) any: Mengambil nilai berbasis request.

Fungsi dalam Package context

context.WithCancel

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

Mengembalikan salinan parent context yang dapat dibatalkan secara manual dengan memanggil fungsi cancel yang dikembalikan.

context.WithDeadline

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)

Mengembalikan salinan parent context yang otomatis dibatalkan setelah deadline terlampaui.

context.WithTimeout

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

Mengembalikan salinan parent context yang otomatis dibatalkan setelah durasi tertentu. WithTimeout setara dengan WithDeadline(parent, time.Now().Add(timeout)).

context.WithValue

func WithValue(parent Context, key, val any) Context

Mengembalikan salinan parent context yang membawa nilai berbasis request.

Best Practices

  • Pass Context Secara Eksplisit:
    • Selalu berikan context.Context sebagai argumen pertama pada fungsi yang menangani operasi yang mungkin memerlukan pembatalan atau deadline.
    • Gunakan context untuk menyebarkan metadata request (misalnya trace ID) melintasi HTTP handler atau gRPC interceptor.
    • Kombinasikan context secara tepat untuk menyebarkan pembatalan atau deadline.
  • Hormati Pembatalan:
    • Periksa ctx.Done() secara periodik dalam operasi jangka panjang dan keluar segera saat dibatalkan.

      select {
      case <-ctx.Done():
          return ctx.Err()
      default:
          // Lanjutkan pemrosesan
      }
  • Gunakan Timeout Secukupnya:
    • Hindari penggunaan context.WithTimeout berlebihan dalam operasi bersarang untuk mencegah deadline yang saling bertentangan. Tetapkan timeout di level yang lebih tinggi dan propagasikan ke bawah.
  • Hindari:
    • Menggunakan context.WithValue untuk Data Kritis: Gunakan WithValue hanya untuk data berbasis request. Hindari menyimpan data kritis atau besar; sebaliknya, berikan data tersebut secara eksplisit.
    • Menggunakan context.Background Di Mana Saja: Gunakan context.Background hanya untuk operasi root-level (misalnya di main atau test).
    • Mutasi Nilai dalam Context: Context bersifat immutable. Hindari mencoba memodifikasi atau mengganti nilai dalam context.

Contoh

package main

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

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
        defer cancel()

        err := doWork(ctx)
        if err != nil {
            http.Error(w, "Request timed out", http.StatusRequestTimeout)
            return
        }
        w.Write([]byte("Success"))
    })

    http.ListenAndServe(":8080", nil)
}

func doWork(ctx context.Context) error {
    select {
    case <-time.After(3 * time.Second): // Simulasi kerja lama
        return nil
    case <-ctx.Done():
        return ctx.Err()
    }
}
  • Pada contoh di atas kami mencoba mensimulasikan kerja lama dan seharusnya merespons dengan error Request timed out.
  • Coba buat timeout lebih lama dan lihat hasilnya.

Referensi


Edit on GitHub

Last updated on

Context | Faisal Affan