← Back to Cecep Azhar
Cecep Azhar #golang#concurrency#goroutines#channels#worker-pool#patterns

Golang Concurrency Patterns untuk Production Systems

Cecep Saeful Azhar • 16 Oct 2026
Golang Concurrency Patterns untuk Production Systems
Panduan lengkap concurrency patterns di Go: goroutines, channels, worker pools, dan error handling. Dari basic sampai advanced patterns.

Go Concurrency: Bukan Sekadar Goroutines

Halo, Cecep Azhar di sini. Salah satu alasan utama saya memilih Go untuk production systems adalah concurrency model-nya yang excellent. Goroutines ringan, channels aman, dan pattern-nya sudah mature. Tapi banyak developer yang hanya tahu permukaan — mereka tahu "pakai go keyword" tapi tidak memahami patterns yang tepat.

Mari kita bahas concurrency patterns yang benar-benar berguna di production.

1. Fan-Out Fan-In Pattern

Pattern ini saya gunakan saat butuh memproses banyak data secara paralel. Saya pernah pakai ini untuk memproses 10,000+ records dalam beberapa detik yang seharusnya butuh 10+ menit secara serial.

func processBatch(items []Item) []Result {
    inputCh := make(chan Item, len(items))
    resultCh := make(chan Result, len(items))

    numWorkers := runtime.NumCPU()
    var wg sync.WaitGroup

    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for item := range inputCh {
                result := processItem(item)
                resultCh <- result
            }
        }()
    }

    for _, item := range items {
        inputCh <- item
    }
    close(inputCh)

    go func() {
        wg.Wait()
        close(resultCh)
    }()

    var results []Result
    for result := range resultCh {
        results = append(results, result)
    }
    return results
}

2. Worker Pool Pattern

Ini pattern paling sering saya gunakan di production. Controlled concurrency — tidak spawn goroutine tanpa batas:

type WorkerPool struct {
    jobs    chan Job
    results chan Result
    workers int
}

func NewWorkerPool(workers int) *WorkerPool {
    return &WorkerPool{
        jobs:    make(chan Job, 100),
        results: make(chan Result, 100),
        workers: workers,
    }
}

func (wp *WorkerPool) Start(ctx context.Context) {
    var wg sync.WaitGroup
    for i := 0; i < wp.workers; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            for {
                select {
                case <-ctx.Done():
                    return
                case job, ok := <-wp.jobs:
                    if !ok {
                        return
                    }
                    result := process(ctx, job)
                    wp.results <- result
                }
            }
        }(i)
    }
    go func() {
        wg.Wait()
        close(wp.results)
    }()
}

3. Pipeline Pattern

Untuk memproses data melalui beberapa tahap secara paralel. Setiap stage berjalan di goroutine terpisah dan terhubung melalui channels:

func pipeline(ctx context.Context, input <-chan Record) <-chan error {
    validated := fetch(ctx, input)
    transformed := validate(ctx, validated)
    stored := transform(ctx, transformed)
    return store(ctx, stored)
}

Error Handling di Concurrency

Ini bagian yang paling sering salah di production. Dari pengalaman saya:

g, ctx := errgroup.WithContext(ctx)

g.Go(func() error {
    return fetchAndProcess(ctx, data)
})

g.Go(func() error {
    return syncExternal(ctx, data)
})

if err := g.Wait(); err != nil {
    log.Error("Pipeline failed", "error", err)
    return err
}

Kesimpulan

Concurrency di Go itu powerful tapi harus hati-hati. Selalu pakai context untuk cancellation, jangan spawn goroutine tanpa batas, dan test dengan race detector (go test -race). Pattern-pattern di atas sudah saya buktikan di production — gunakan sesuai kebutuhan Anda.

← More articles from Cecep Azhar Central Content Hub Home →