ProgrammationDéveloppeur Backend Go

Comment fonctionne le package intégré context en Go ? À quoi sert-il, comment créer et terminer correctement des contextes ? Quelles erreurs sont commises lors de son utilisation ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

Le package context est une norme pour gérer la durée de vie (annulation, timeout) et transmettre des métadonnées entre différentes parties du code, notamment lors du travail avec des goroutines et des appels externes (HTTP, DB).

Création :

  • context.Background() — contexte racine
  • context.WithCancel(parent) — nouveau avec annulation
  • context.WithTimeout(parent, duration) — avec timeout
  • context.WithValue(parent, key, value) — avec des données

Il est important de terminer le contexte pour libérer correctement les ressources. Exemple :

ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() // ensuite ctx est utilisé dans une goroutine/requête HTTP

Le modèle typique consiste à transmettre le context en premier argument des fonctions, exemple :

func process(ctx context.Context) error { ... }

Question piège

Quand peut-on transmettre context.TODO() et context.Background() au lieu d'un vrai context, et quelle est la différence entre eux ?

Beaucoup utilisent context.Background() comme placeholder. Mais :

  • context.Background() — racine de l'arbre, utilisez-le uniquement dans main() et les tests, lors de l'initialisation de l'arbre de "contextes".
  • context.TODO() est nécessaire si on ne sait pas encore quel devrait être le contexte — pour des besoins temporaires lors du refactoring. Dans le code de production, TODO est inacceptable : il faut savoir précisément ce qui est transmis et où.

Exemples d'erreurs réelles dues à l'ignorance des subtilités du sujet


Histoire

Dans un microservice, ils ont oublié d'appeler cancel() après avoir créé context.WithTimeout() — les requêtes se bloquaient, les goroutines complétées laissaient des minuteurs "pendus" dans le runtime, ce qui entraînait une fuite de mémoire.


Histoire

La transmission de context.Background() au lieu du contexte reçu à partir de la requête dans les handlers perdait toute la chaîne d'annulation et le trace-id, ce qui faisait que les limites de temps ne fonctionnaient pas et que les annulations de requêtes ne se produisaient pas.


Histoire

Via context.WithValue(), les développeurs ont transmis des données, mais les clés étaient des chaînes. En conséquence, en raison de possibles collisions de clés provenant de différents packages, des erreurs inattendues survenaient ("clé déjà occupée"). La bonne pratique : utiliser des types uniques pour les clés.