Understanding Golang Context

Table Of Contents

  • Background
  • Case 1: Store & Progate Data
  • Case 2:

Background

I always thought, if I want to understand something, I need to find the problems and try to solve it. That’s why I want write this post with the study case approach. Anyway, context is an important fundamental that we should understood when we use Go as programming languange. It’s so relevant with the http request, so the problem that I want to show you is related with the http request.

Case 1: Store & Progate Data

I want to log the process of each request in backend application, so when there’s problems, the monitoring team can easily to trace problems.

❯ go run .
2026/09/19 13:57:10 INFO [Handler] Receive request trx=TRX001 trace=trace-3459
2026/09/19 13:57:10 INFO [Service] Process trx=TRX001
2026/09/19 13:57:10 INFO [callPSP] Call to Payment Provider trx=TRX001 merchant=MERCHANT-A trace=trace-3459
2026/09/19 13:57:10 INFO [Handler] Receive request trx=TRX003 trace=trace-5602
2026/09/19 13:57:10 INFO [Handler] Receive request trx=TRX002 trace=trace-9118
2026/09/19 13:57:10 INFO [Service] Process trx=TRX002
2026/09/19 13:57:10 INFO [callPSP] Call to Payment Provider trx=TRX002 merchant=MERCHANT-B trace=trace-9118
2026/09/19 13:57:10 INFO [Service] Process trx=TRX003
2026/09/19 13:57:10 INFO [callPSP] Call to Payment Provider trx=TRX003 merchant=MERCHANT-C trace=trace-5602
[LOGGER]   trx=TRX001 status=SUCCESS trace=trace-3459
[LOGGER]   trx=TRX003 status=SUCCESS trace=trace-5602
[LOGGER]   trx=TRX002 status=SUCCESS trace=trace-9118

Notes

  • there’s go convention about not using “Set” prefix to function naming

Case 2: Timeout Management

Goroutine Leak

**Phrapse “for example” it’s too boring

User want to checkout the transaction, Payment service will be hitted by the Client. Then the payment service need to call 3 services before motong saldo user.

  1. Check Fraud - Risk Service
  2. Check Promo - Promo Service
  3. Call Payment Provider Service to create order

But the Promo service is down, bikin request-nya hanging dan gak balikin response

Archived

When user pay, Payment Provider will notify to our service (in this case we named as Payment Service). That bank will hit Payment Service, and Payment Service need to return as fast as possible so thet Payment Provider not menganggap webhooknya failed/timeout. We try to “Ok, I accept the webhook, I need to return 200 OK to bank directly. But for the query update transaction and call notification service will I lempar to go func in background.

There’s flash sale event, the Bank send ribuan callback per detik. In background will spawn ribuan goroutine bersamaan to update DB and call notification service If the query is slow or the notification call is slow to return response, then ini akan membuat goroutine tersebut tertahan di memori dan memicu OOM kiled.

Outline

What is golang context

  • the context.Context interface — carries cancellation signal, deadline, and request-scoped values
  • its 4 methods: Deadline(), Done(), Err(), Value()
  • context is not a bag of options — its real job is propagating cancellation through a call tree
  • how it flows: first parameter of every function that can be cancelled

What problems does it solve

  • goroutine leak — client disconnects, background work keeps running
  • no timeout — downstream call hangs forever, goroutines pile up
  • no cancellation propagation — parent stops, children keep working
  • no request-scoped values (trace id, user id)

Context types

  • context.Background() and context.TODO() — the roots
  • context.WithCancel — manual cancellation
  • context.WithTimeout / context.WithDeadline — auto-cancel after a duration / at a time
  • context.WithValue — request-scoped data (use sparingly)
  • the tree model — children inherit the parent’s cancellation

Practical — swimmy simulation

  • simulate the problems and the fixes at github.com/husfuu/swimmy
  • run: go run . inside the repo, then curl the endpoints
  • problem endpoints:
    • /leak — handler fires a 30s goroutine that ignores ctx; goroutine count stays up after the response returns → goroutine leak
    • /timeout — handler blocks 30s in slowDBBlocking with no context; curl --max-time 3 gives up but the server keeps sleeping → no timeout
  • fixed endpoints:
    • /leak-ok — context.WithCancel(r.Context()) + select on ctx.Done(); goroutine stops as soon as the client disconnects
    • /timeout-ok — context.WithTimeout(..., 2*time.Second); returns 504 via ctx.Err() after ~2s
  • key files: problems.go, solutions.go, slow.go (the fake DB/API work), main.go (server + goroutine count logging)
  • observe it yourself: each request logs the goroutine count afterward, so you can watch the leak vs. the fix

Rules of thumb

  • ctx is always the first parameter
  • never store context in a struct
  • never pass nil context — use Background() / TODO()
  • always call cancel (defer cancel() right after creating the context)
  • check cancellation with errors.Is(err, context.Canceled / context.DeadlineExceeded)