Context

Context adalah package yang menyediakan cara untuk membawa deadline, cancellation signals, dan value yang scoped ke request.

Contoh Masalah

Bagaimana cara:

  1. Mengelola request lifecycle
  2. Membatalkan operasi
  3. Menyimpan request-scoped values

Penyelesaian

package main

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

// 1. Basic context with timeout
func slowOperation(ctx context.Context) error {
    select {
    case <-time.After(2 * time.Second):
        fmt.Println("Slow operation completed")
        return nil
    case <-ctx.Done():
        return ctx.Err()
    }
}

// 2. Context with value
type contextKey string

func withUser(ctx context.Context, username string) context.Context {
    return context.WithValue(ctx, contextKey("user"), username)
}

func getUser(ctx context.Context) string {
    value := ctx.Value(contextKey("user"))
    if username, ok := value.(string); ok {
        return username
    }
    return ""
}

// 3. HTTP server with context
func handleRequest(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    
    // Add timeout
    ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
    defer cancel()

    // Add user info
    ctx = withUser(ctx, "john_doe")

    // Do some work
    select {
    case <-time.After(2 * time.Second):
        fmt.Fprintln(w, "Request processed")
    case <-ctx.Done():
        fmt.Fprintln(w, "Request cancelled")
        return
    }
}

// 4. Worker pool with context
func worker(ctx context.Context, id int, jobs <-chan int, results chan<- int) {
    for {
        select {
        case <-ctx.Done():
            return
        case j, ok := <-jobs:
            if !ok {
                return
            }
            fmt.Printf("worker %d started job %d\n", id, j)
            time.Sleep(time.Second)
            results <- j * 2
        }
    }
}

// 5. Database operation simulation
type DB struct{}

func (db *DB) QueryWithContext(ctx context.Context, query string) (string, error) {
    select {
    case <-time.After(500 * time.Millisecond):
        return "query result", nil
    case <-ctx.Done():
        return "", ctx.Err()
    }
}

// 6. Chain of operations with context
func operation1(ctx context.Context) error {
    select {
    case <-time.After(100 * time.Millisecond):
        fmt.Println("Operation 1 completed")
        return nil
    case <-ctx.Done():
        return ctx.Err()
    }
}

func operation2(ctx context.Context) error {
    select {
    case <-time.After(200 * time.Millisecond):
        fmt.Println("Operation 2 completed")
        return nil
    case <-ctx.Done():
        return ctx.Err()
    }
}

func operation3(ctx context.Context) error {
    select {
    case <-time.After(300 * time.Millisecond):
        fmt.Println("Operation 3 completed")
        return nil
    case <-ctx.Done():
        return ctx.Err()
    }
}

func main() {
    // Contoh 1: Context with timeout
    fmt.Println("Context with Timeout Example:")
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()

    if err := slowOperation(ctx); err != nil {
        fmt.Printf("Error: %v\n", err)
    }

    // Contoh 2: Context with value
    fmt.Printf("\nContext with Value Example:\n")
    ctx = withUser(context.Background(), "alice")
    fmt.Printf("User from context: %s\n", getUser(ctx))

    // Contoh 3: HTTP server
    fmt.Printf("\nHTTP Server Example:\n")
    http.HandleFunc("/", handleRequest)
    go func() {
        log.Fatal(http.ListenAndServe(":8080", nil))
    }()

    // Contoh 4: Worker pool
    fmt.Printf("\nWorker Pool Example:\n")
    ctx, cancel = context.WithCancel(context.Background())
    
    const numJobs = 5
    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)

    // Start workers
    for w := 1; w <= 3; w++ {
        go worker(ctx, w, jobs, results)
    }

    // Send jobs
    for j := 1; j <= numJobs; j++ {
        jobs <- j
    }
    close(jobs)

    // Collect results
    for a := 1; a <= numJobs; a++ {
        <-results
    }
    cancel()

    // Contoh 5: Database operation
    fmt.Printf("\nDatabase Operation Example:\n")
    db := &DB{}
    ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second)
    defer cancel()

    result, err := db.QueryWithContext(ctx, "SELECT * FROM users")
    if err != nil {
        fmt.Printf("Query error: %v\n", err)
    } else {
        fmt.Printf("Query result: %s\n", result)
    }

    // Contoh 6: Chain of operations
    fmt.Printf("\nChain of Operations Example:\n")
    ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second)
    defer cancel()

    if err := operation1(ctx); err != nil {
        fmt.Printf("Operation 1 failed: %v\n", err)
        return
    }

    if err := operation2(ctx); err != nil {
        fmt.Printf("Operation 2 failed: %v\n", err)
        return
    }

    if err := operation3(ctx); err != nil {
        fmt.Printf("Operation 3 failed: %v\n", err)
        return
    }

    fmt.Println("All operations completed successfully")
}

Penjelasan Kode

  1. Basic Context

    • Background context
    • WithTimeout
    • WithCancel
    • WithDeadline
  2. Context Values

    • WithValue
    • Type-safe keys
    • Request-scoped data
  3. Common Uses

    • HTTP requests
    • Database operations
    • Worker pools

Output

Context with Timeout Example:
Slow operation completed

Context with Value Example:
User from context: alice

Worker Pool Example:
worker 1 started job 1
worker 2 started job 2
worker 3 started job 3
worker 1 started job 4
worker 2 started job 5

Database Operation Example:
Query result: query result

Chain of Operations Example:
Operation 1 completed
Operation 2 completed
Operation 3 completed
All operations completed successfully

Tips

  • Selalu cancel context
  • Gunakan context.Background()
  • Hindari menyimpan context
  • Pass context sebagai parameter pertama
  • Gunakan type-safe context keys