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

Timeout & deadline Timeout & deadline

WithTimeout et WithDeadline posent une limite de temps à un arbre de goroutines. Le defer cancel() n'est pas optionnel : l'oublier, c'est fuir un contexte et les ressources qu'il tenait. Le délai qui descend, et le nettoyage qui remonte. WithTimeout and WithDeadline put a time limit on a tree of goroutines. The defer cancel() isn't optional: forgetting it leaks a context and the resources it held. The deadline flowing down, the cleanup flowing back up.

AudienceAudience
Dev qui oublie defer cancel(), ou dont les timeouts en cascade créent des DeadlineExceeded inexpliqués Dev who forgets defer cancel(), or whose cascading timeouts create unexplained DeadlineExceededs
Format
Self-paced
ChapitresChapters
5
Date
Avr 2028 Apr 2028
≈ 15 min ●●○○ TimeoutDeadlinecontext

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

WithTimeout et WithDeadline posent une limite de temps à un arbre entier. Le defer cancel() n'est pas une option.WithTimeout and WithDeadline put a time limit on a whole tree. The defer cancel() is not optional.

Le numéro précédent a posé context.Context comme câble d'annulation explicite. Ce numéro ajoute la dimension temporelle : comment poser une limite de temps à tout un arbre de goroutines. context.WithTimeout et context.WithDeadline créent tous les deux un contexte-fils qui s'annule automatiquement à une échéance — WithTimeout exprime une durée relative (dans 5 secondes), WithDeadline exprime une heure absolue (à 14h30:00). Sous le capot, WithTimeout est simplement WithDeadline(ctx, time.Now().Add(d)) : les deux produisent exactement le même contexte. La différence n'est qu'expressive — durée vs instant — et le bon choix dépend de ce qu'on connaît au moment de l'appel. Ce qui les unit, c'est leur second retour : une fonction cancel. Cette fonction cancel sert à deux choses simultanées. Primo, elle annule le contexte-fils avant son échéance si on finit plus tôt — économiser le timer. Secundo, et c'est la raison pour laquelle elle n'est jamais optionnelle : si on ne l'appelle pas, Go maintient un timer du runtime et des entrées dans l'arbre des contextes jusqu'à ce que le parent s'annule. C'est une fuite discrète, invisible sous faible charge, catastrophique à l'échelle. Le defer cancel() en ligne 2 est donc la règle absolue : créer, différer l'annulation, s'en occuper jamais plus.The previous issue laid context.Context as an explicit cancellation cable. This issue adds the temporal dimension: how to put a time limit on a whole goroutine tree. context.WithTimeout and context.WithDeadline both create a child context that auto-cancels at a deadline — WithTimeout expresses a relative duration (in 5 seconds), WithDeadline expresses an absolute time (at 2:30pm). Under the hood, WithTimeout is simply WithDeadline(ctx, time.Now().Add(d)): both produce exactly the same context. The difference is only expressive — duration vs instant — and the right choice depends on what you know at the call site. What unites them is their second return value: a cancel function. This cancel function serves two simultaneous purposes. First, it cancels the child context before its deadline if you finish sooner — saves the timer. Second, and this is why it's never optional: if you don't call it, Go maintains a runtime timer and entries in the context tree until the parent cancels. It's a discreet leak, invisible under light load, catastrophic at scale. The defer cancel() on line 2 is therefore the absolute rule: create, defer the cancellation, never think about it again.

WithTimeout et WithDeadline : deux expressions, même résultatWithTimeout and WithDeadline: two expressions, same result
// WithTimeout : dans combien de temps ?
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()   // libère le timer même si on finit avant

// WithDeadline : à quel moment absolu ?
deadline := time.Now().Add(5 * time.Second)
ctx, cancel := context.WithDeadline(ctx, deadline)
defer cancel()

// Les deux produisent le même contexte — c'est l'expression qui diffère.
// ctx.Deadline() retourne l'heure d'expiration dans les deux cas.
La limite descend, le nettoyage remonteThe limit flows down, the cleanup flows back up
WithTimeout
crée l'échéance
ctx.Done()
se ferme à l'expiration
defer cancel()
libère le timer

Le délai descend dans l'arbre via le contexte. Le cancel() remonte dans le code via le defer — toujours en deuxième ligne après WithTimeout.The deadline flows down the tree via the context. The cancel() comes back in code via defer — always the second line after WithTimeout.

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

« Ai-je créé un contexte avec timeout ou deadline ? Alors la ligne suivante est defer cancel(). » Sans exception — même si on sait que le contexte sera annulé par le parent avant l'échéance, parce que defer cancel() sur un contexte déjà annulé est un no-op, et qu'omettre un defer cancel() sur un contexte vivant est une fuite silencieuse. La règle vaut dans les tests comme en production."Did I create a context with timeout or deadline? Then the next line is defer cancel()." No exception — even if you know the context will be canceled by the parent before the deadline, because defer cancel() on an already-canceled context is a no-op, and omitting a defer cancel() on a live context is a silent leak. The rule holds in tests as in production.

Quand choisir WithTimeout vs WithDeadline ?When to choose WithTimeout vs WithDeadline?

WithTimeout convient quand on connaît une durée — « cette requête SQL ne doit pas dépasser 3 secondes ». WithDeadline convient quand on a une heure cible déjà calculée — un budget de délai partagé entre plusieurs sous-appels, ou une coordination avec une horloge externe. En pratique, WithTimeout couvre 90 % des cas parce que les délais sont le plus souvent relatifs au moment de l'appel. Mais si la couche supérieure a déjà calculé une deadline absolue et la passe en paramètre, préférer WithDeadline évite de la recalculer en durée.WithTimeout suits when you know a duration — 'this SQL query must not exceed 3 seconds.' WithDeadline suits when you have an already-computed target time — a deadline budget shared across several sub-calls, or coordination with an external clock. In practice, WithTimeout covers 90% of cases because deadlines are most often relative to the call moment. But if the upper layer has already computed an absolute deadline and passes it as a parameter, preferring WithDeadline avoids recomputing it as a duration.

🔒

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