Vol. 8 — № 01
The Go Loop · KiosqueNewsstand
Blog  
Un atelier Go · Édition d'ApprentissageA Go Workshop · Learning Edition

context.Context, l'annulation qui descend context.Context, cancellation flowing down

context.Context est le câble qui traverse les couches : du handler HTTP jusqu'à la requête SQL. Premier argument, jamais stocké, toujours propagé. Quand le haut annule, tout ce qui en dépend l'apprend par Done(). context.Context is the cable threading through layers: from the HTTP handler down to the SQL query. First argument, never stored, always propagated. When the top cancels, everything below hears it through Done().

AudienceAudience
Dev qui stocke ctx dans une struct, ou dont les goroutines ne s'arrêtent pas quand le client raccroche Dev who stores ctx in a struct, or whose goroutines don't stop when the client hangs up
Format
Self-paced
ChapitresChapters
5
Date
Mar 2028 Mar 2028
≈ 17 min ●●○○ contextAnnulation

Chapitre 1 en accès libre — la suite (ch. 2 à 5) est réservée. Chapter 1 free to read — the rest (ch. 2–5) is members-only.

01CadrageFraming3 min

context.Context est le câble qui descend de couche en couche. Premier argument, jamais stocké, toujours propagé.context.Context is the cable threading down layer by layer. First argument, never stored, always propagated.

Les volumes précédents ont couvert comment une fonction gère sa propre sortie — defer, capture, recover. Mais un programme concurrent réel n'est pas une fonction : c'est un arbre de goroutines lancé pour une même tâche. Un handler HTTP appelle un service, qui interroge une base, qui démarre deux goroutines de cache. Quand le client raccroche — ou que le délai expire — tout ce travail doit cesser. Personne ne peut appeler individuellement chaque goroutine. Il faut un signal qui descend : context.Context est exactement ce signal. Un contexte est une valeur immuable qui forme un arbre : context.Background() est la racine, et chaque dérivé — WithCancel, WithTimeout, WithValue — crée un nœud fils. Quand un nœud est annulé, tous ses descendants le savent immédiatement via leur canal Done(). Le contexte n'est pas un objet global, une variable d'environnement, ni un champ de struct : c'est le premier argument de chaque fonction qui fait du travail annulable. La convention Go est stricte : ctx context.Context, toujours en tête de signature, toujours passé explicitement. Ce qui semble verbeux est en réalité une propriété de sécurité : chaque site d'appel voit qu'il participe à une annulation, et choisit consciemment quel contexte transmettre.Previous volumes covered how a function manages its own exit — defer, capture, recover. But a real concurrent program isn't a function: it's a tree of goroutines launched for one task. An HTTP handler calls a service, which queries a database, which starts two cache goroutines. When the client hangs up — or the deadline expires — all that work must cease. Nobody can call each goroutine individually. You need a signal that flows down: context.Context is exactly that signal. A context is an immutable value forming a tree: context.Background() is the root, and each derivative — WithCancel, WithTimeout, WithValue — creates a child node. When a node is canceled, all its descendants know immediately via their Done() channel. A context is not a global object, an environment variable, or a struct field: it's the first argument of every function doing cancelable work. The Go convention is strict: ctx context.Context, always first in the signature, always passed explicitly. What seems verbose is actually a safety property: every call site sees that it participates in a cancellation, and consciously chooses which context to forward.

Le câble qui traverse les couchesThe cable threading through the layers
// Le câble traverse les couches : on ne le stocke jamais, on le passe.
func handleSearch(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()              // racine : le contexte de la requête HTTP
    results, err := search(ctx, r.URL.Query().Get("q"))
    // …
}

func search(ctx context.Context, query string) ([]Result, error) {
    rows, err := db.QueryContext(ctx, "SELECT …", query)  // ctx descend jusqu'à SQL
    if err != nil { return nil, err }
    // …
}
Un arbre de contextesA tree of contexts
context.Background()
la racine
r.Context()
la requête HTTP
WithCancel / WithTimeout
les fils

Annuler un nœud parent propage le signal à tous ses descendants. Jamais l'inverse. Le contexte descend, l'annulation descend.Canceling a parent node propagates the signal to all its descendants. Never the reverse. The context flows down, cancellation flows down.

Le réflexe du numéroThe issue's reflex

« Est-ce que cette fonction fait du travail qui peut durer ? Si oui, elle accepte un ctx en premier argument et le passe à ses sous-appels. » context.Context n'est pas une option — c'est le fil conducteur de toute opération annulable, de la requête HTTP jusqu'au dernier appel SQL. Une fonction qui l'ignore isole une partie du travail du signal d'annulation — et cette partie continuera à tourner même après que tout le reste s'est arrêté."Does this function do work that can last? If yes, it takes a ctx as first argument and passes it to its sub-calls." context.Context is not an option — it's the conductor of every cancelable operation, from the HTTP request down to the last SQL call. A function that ignores it isolates part of the work from the cancellation signal — and that part will keep running even after everything else has stopped.

Pourquoi immuable ?Why immutable?

Un contexte ne se modifie jamais en place. WithCancel, WithTimeout et WithValue créent chacun un nouveau contexte-fils qui pointe vers son parent. Cette immuabilité garantit qu'un fils ne peut pas altérer son parent : la couche service peut restreindre son délai sans risquer d'annuler le contexte du handler. L'arbre ne peut être élagué que depuis la racine vers les feuilles, jamais l'inverse. C'est ce qui rend la propagation sûre dans un programme concurrent : plusieurs goroutines peuvent lire le même contexte simultanément, sans synchronisation, car rien ne l'écrit.A context never mutates in place. WithCancel, WithTimeout, and WithValue each create a new child context pointing to its parent. This immutability guarantees a child can't alter its parent: the service layer can tighten its deadline without risking canceling the handler's context. The tree can only be pruned from root toward leaves, never the reverse. That's what makes propagation safe in a concurrent program: multiple goroutines can read the same context simultaneously, without synchronization, because nothing writes to it.

🔒

La suite est réservée The rest is members-only

Le premier numéro est libre. Débloque tout The Go Loop — tous les volumes, à vie — pour 5 €, paiement unique. The first issue is free. Unlock all of The Go Loop — every volume, forever — for €5, one-time.

Retour au kiosqueBack to newsstand