El paquete context es el estándar para gestionar el tiempo de vida (cancelación, tiempo de espera) y la transmisión de metadatos entre partes del código, especialmente al trabajar con goroutine y llamadas externas (HTTP, DB).
Creación:
Es importante finalizar el contexto para liberar los recursos correctamente. Ejemplo:
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() // luego ctx se usa en goroutine/petición HTTP
El patrón típico es pasar el contexto como el primer argumento de las funciones, ejemplo:
func process(ctx context.Context) error { ... }
¿Cuándo se puede pasar context.TODO() y context.Background() en lugar de un contexto real, y cuál es la diferencia entre ellos?
Muchos utilizan context.Background() como un marcador de posición. Pero:
context.Background() — es la raíz del árbol, utilícelo solo en main() y pruebas, al inicializar el "árbol de contextos".context.TODO() es necesario si aún no está claro cómo debería ser el contexto — para "tapar agujeros" temporalmente durante la refactorización. En el código de producción, TODO es inaceptable: se debe saber con precisión qué se está pasando y a dónde.Historia
En un microservicio se olvidó llamar a cancel() después de crear context.WithTimeout() — las peticiones se detenían, las goroutines finalizadas dejaban "timers colgando" en tiempo de ejecución, lo que ocasionaba una fuga de memoria.
Historia
Pasar context.Background() en lugar del contexto proveniente de la solicitud en los handlers perdía toda la cadena de cancelación y trace-id, lo que hacía que los límites de tiempo de espera no funcionaran y las cancelaciones de peticiones no se activaran.
Historia
A través de context.WithValue() los desarrolladores pasaban datos, pero las claves eran cadenas. Como resultado, debido a posibles colisiones de claves de diferentes paquetes, surgían errores inesperados ("clave ya ocupada"). La práctica correcta: utilizar tipos únicos para las claves.