Around Go: Gin services · 3 MIN READ

Gin contexts and goroutines: extract a job, do not retain the response

Give background work a bounded owner without keeping a pooled Gin context alive.

Gin reuses contexts after a handler returns. Passing the original *gin.Context into a goroutine can observe another request’s mutable state. Copy is useful for limited read-only context access, but it does not extend the response writer’s lifetime, clone every referenced object or give detached work a durable owner. Prefer extracting immutable job input and handing it to a deliberately managed queue.

gin · A MAP OF THE IDEA

  1. Extract immutable input
  2. Bound admission
  3. Worker owns lifetime
  4. Join or recover durable job

Useful when

  • Give background work a bounded owner without keeping a pooled Gin context alive.
  • Makes the owner and the failure boundary explicit.

Watch out

  • A c.Copy call does not make asynchronous response writes safe or turn the request context into a process context.
  • Run native service and failure tests in your own development environment.

Implementation & operation cost: Additional state and tests must be justified by the demonstrated failure; this lesson is not a requirement for every Go program.

01

The contract and the failure

Gin reuses contexts after a handler returns. Passing the original *gin.Context into a goroutine can observe another request’s mutable state. Copy is useful for limited read-only context access, but it does not extend the response writer’s lifetime, clone every referenced object or give detached work a durable owner. Prefer extracting immutable job input and handing it to a deliberately managed queue.

02

Work through the example

For best-effort work, state its lifetime and cancellation policy. For must-complete work, persist a job before acknowledging acceptance and use idempotent processing. Copy only immutable data required by the worker; slices and maps need an ownership decision. Request cancellation is appropriate for request work, while detached work needs an application-owned context with its own deadline and shutdown path.

go
package main

import (
	"fmt"
	"net/http/httptest"
	"sync"

	"github.com/gin-gonic/gin"
)

type Job struct{ Path string }

func main() {
	gin.SetMode(gin.TestMode)
	jobs := make(chan Job, 1)
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		for job := range jobs {
			fmt.Println("work", job.Path)
		}
	}()
	r := gin.New()
	r.POST("/jobs", func(c *gin.Context) {
		job := Job{Path: c.Request.URL.Path}
		select {
		case jobs <- job:
			c.Status(202)
		default:
			c.Status(503)
		}
	})
	w := httptest.NewRecorder()
	r.ServeHTTP(w, httptest.NewRequest("POST", "/jobs", nil))
	fmt.Println("accepted", w.Code)
	close(jobs)
	wg.Wait()
}
Read the code

The handler sends a copied string in a small value and never lets the worker use c.Writer. The channel has a fixed capacity and the process joins its worker. Output order of accepted versus work is intentionally unspecified. This queue is in-memory: returning 202 here does not promise survival of a process crash.

03

Production decisions

A c.Copy call does not make asynchronous response writes safe or turn the request context into a process context. Unbounded goroutines are an unbounded queue. Sending to a channel while another owner closes it can panic. Test queue-full rejection, shutdown while a handler is enqueueing, duplicate jobs and process recovery separately from ordinary request success.

04

Explain it in an interview

Does c.Copy make it safe to write a response after the handler returns? No. Extract the data a worker needs, never reuse the completed response writer, and define queue capacity, lifetime and durability. Describe the state before the operation, its owner, the failure between steps and the evidence you would test. Distinguish what this example demonstrates from the infrastructure a production service still needs.

TAKE THIS WITH YOU

Give background work a bounded owner without keeping a pooled Gin context alive. No. Extract the data a worker needs, never reuse the completed response writer, and define queue capacity, lifetime and durability.

Check your understanding

Does c.Copy make it safe to write a response after the handler returns?

Go to the source

Progress stays in this browser. No account needed.