A context can carry request metadata into a goroutine without using shared global state.

context value Pass a context into a worker goroutine and send the derived label back through a channel.

Context Worker Value

requestID
context_worker_value.go
Replay: real traced execution (multi-file project)
package main

import (
	"context"
	"fmt"
)

func main() {
	var requestID = "A1"
	ctx := context.WithValue(context.Background(), "request", requestID)
	done := make(chan string, 1)

	go func(workerContext context.Context) {
		value := workerContext.Value("request").(string)
		done <- "handled:" + value
	}(ctx)

	message := <-done

	fmt.Println("requestID=", requestID)
	fmt.Println("message=", message)
	fmt.Println("done=", true)
}
package main

import (
	"context"
	"fmt"
)

func main() {
	var requestID = "B2"
	ctx := context.WithValue(context.Background(), "request", requestID)
	done := make(chan string, 1)

	go func(workerContext context.Context) {
		value := workerContext.Value("request").(string)
		done <- "handled:" + value
	}(ctx)

	message := <-done

	fmt.Println("requestID=", requestID)
	fmt.Println("message=", message)
	fmt.Println("done=", true)
}
package main

import (
	"context"
	"fmt"
)

func main() {
	var requestID = "C3"
	ctx := context.WithValue(context.Background(), "request", requestID)
	done := make(chan string, 1)

	go func(workerContext context.Context) {
		value := workerContext.Value("request").(string)
		done <- "handled:" + value
	}(ctx)

	message := <-done

	fmt.Println("requestID=", requestID)
	fmt.Println("message=", message)
	fmt.Println("done=", true)
}
  1. requestID ← "A1", ctx ← &context.valueCtx{Context:context.backgroundCtx{emptyCtx:context.emptyCtx{}}, key:"request", val:"A1"}

    8func main() {9  var requestID→ "A1" = "A1" //@requestID="B2", "C3"10  ctx→ &context.valueCtx{Context:context.backgroundCtx{emptyCtx:context.emptyCtx{}}, key:"request", val:"A1"} := context.WithValue(context.Background(), "request", requestID"A1")11  done→ (chan string)(⟨addr A⟩) := make(chan string, 1)1213  go func(workerContext context.Context) {14    value := workerContext.Value("request").(string)15    done <- "handled:" + value16  }(ctx&context.valueCtx{Context:context.backgroundCtx{emptyCtx:context.emptyCtx{}}, key:"request", val:"A1"})1718  message := <-done(chan string)(⟨addr A⟩)
  2. value ← "A1"

    13go func(workerContext&context.valueCtx{Context:context.backgroundCtx{emptyCtx:context.emptyCtx{}}, key:"request", val:"A1"} context.Context) {14  value→ "A1" := workerContext.Value("request").(string)15  done(chan string)(⟨addr A⟩) <- "handled:" + value"A1"16}(ctx)
  3. message ← "handled:A1"

    18  message→ "handled:A1" := <-done(chan string)(⟨addr A⟩)1920  fmt.Println("requestID=", requestID"A1")21  fmt.Println("message=", message"handled:A1")22  fmt.Println("done=", true)23}
    outputrequestID= A1
    message= handled:A1
    done= true
  1. requestID ← "B2", ctx ← &context.valueCtx{Context:context.backgroundCtx{emptyCtx:context.emptyCtx{}}, key:"request", val:"B2"}

    8func main() {9  var requestID→ "B2" = "B2"10  ctx→ &context.valueCtx{Context:context.backgroundCtx{emptyCtx:context.emptyCtx{}}, key:"request", val:"B2"} := context.WithValue(context.Background(), "request", requestID"B2")11  done→ (chan string)(⟨addr A⟩) := make(chan string, 1)1213  go func(workerContext context.Context) {14    value := workerContext.Value("request").(string)15    done <- "handled:" + value16  }(ctx&context.valueCtx{Context:context.backgroundCtx{emptyCtx:context.emptyCtx{}}, key:"request", val:"B2"})1718  message := <-done(chan string)(⟨addr A⟩)
  2. value ← "B2"

    13go func(workerContext&context.valueCtx{Context:context.backgroundCtx{emptyCtx:context.emptyCtx{}}, key:"request", val:"B2"} context.Context) {14  value→ "B2" := workerContext.Value("request").(string)15  done(chan string)(⟨addr A⟩) <- "handled:" + value"B2"16}(ctx)
  3. message ← "handled:B2"

    18  message→ "handled:B2" := <-done(chan string)(⟨addr A⟩)1920  fmt.Println("requestID=", requestID"B2")21  fmt.Println("message=", message"handled:B2")22  fmt.Println("done=", true)23}
    outputrequestID= B2
    message= handled:B2
    done= true
  1. requestID ← "C3", ctx ← &context.valueCtx{Context:context.backgroundCtx{emptyCtx:context.emptyCtx{}}, key:"request", val:"C3"}

    8func main() {9  var requestID→ "C3" = "C3"10  ctx→ &context.valueCtx{Context:context.backgroundCtx{emptyCtx:context.emptyCtx{}}, key:"request", val:"C3"} := context.WithValue(context.Background(), "request", requestID"C3")11  done→ (chan string)(⟨addr A⟩) := make(chan string, 1)1213  go func(workerContext context.Context) {14    value := workerContext.Value("request").(string)15    done <- "handled:" + value16  }(ctx&context.valueCtx{Context:context.backgroundCtx{emptyCtx:context.emptyCtx{}}, key:"request", val:"C3"})1718  message := <-done(chan string)(⟨addr A⟩)
  2. value ← "C3"

    13go func(workerContext&context.valueCtx{Context:context.backgroundCtx{emptyCtx:context.emptyCtx{}}, key:"request", val:"C3"} context.Context) {14  value→ "C3" := workerContext.Value("request").(string)15  done(chan string)(⟨addr A⟩) <- "handled:" + value"C3"16}(ctx)
  3. message ← "handled:C3"

    18  message→ "handled:C3" := <-done(chan string)(⟨addr A⟩)1920  fmt.Println("requestID=", requestID"C3")21  fmt.Println("message=", message"handled:C3")22  fmt.Println("done=", true)23}
    outputrequestID= C3
    message= handled:C3
    done= true