context.Context: La cancelación como API de primera clase
context.Context es cómo los servicios de Go detienen el trabajo de forma cooperativa cuando un cliente se va, expira un plazo o un proceso se apaga.
Busca en todas las páginas de la documentación
context.Context es cómo los servicios de Go detienen el trabajo de forma cooperativa cuando un cliente se va, expira un plazo o un proceso se apaga.
No es un azúcar opcional en el código del servidor; es el contrato estándar para tiempos de espera, cancelación y metadatos con ámbito de solicitud en manejadores HTTP, llamadas a bases de datos, gRPC y trabajadores en segundo plano.
Conceptos básicos de context recopila fragmentos ejecutables; los artículos hermanos cubren la propagación HTTP, los plazos entre servicios, los valores, la integración con bases de datos/gRPC, las pruebas y las convenciones del equipo.
context.Context es un identificador inmutable que se pasa por las pilas de llamadas. Señala cuándo debe detenerse el trabajo y transporta un pequeño conjunto de valores con ámbito de solicitud.ctx context.Context como su primer parámetro.database/sql, errgroup, apagado elegante, registro estructurado con IDs de solicitud.Imagina una solicitud que entra en tu servicio como un árbol de llamadas a funciones.
En la raíz, algo crea un contexto: http.Request.Context() para HTTP, context.Background() para el inicio del proceso o context.WithTimeout para un trabajo limitado.
Cada llamada descendente recibe ese contexto (o un hijo derivado de él).
Cuando la raíz se cancela (desconexión del cliente, tiempo de espera agotado o cancel() explícito), todos los descendientes que respetan ctx.Done() deben detenerse rápidamente.
Un contexto transporta tres preocupaciones:
Done() se cierra cuando el trabajo debe terminar.Deadline().Los contextos forman una cadena padre-hijo.
context.WithCancel(parent) devuelve un hijo y una función cancel.
Llamar a cancel() en el hijo no cancela al padre, pero cancelar al padre cancela a todos los descendientes.
Esa propagación unidireccional es deliberada: el upstream es el propietario del ciclo de vida.
La firma de función idiomática es:
func FetchUser(ctx context.Context, id string) (*User, error)Los llamadores pasan ctx sin cambios o lo envuelven:
ctx, cancel := context.WithTimeout(parent, 2*time.Second)
defer cancel()El código bloqueante debe seleccionar en ctx.Done() o usar APIs que acepten contexto (db.QueryContext, stubs gRPC, http.NewRequestWithContext).
Cuando ctx termina, devuelve ctx.Err() - generalmente context.Canceled o context.DeadlineExceeded.
Los servidores HTTP conectan esto automáticamente.
net/http da a cada solicitud r.Context(), que se cancela cuando el cliente cierra la conexión o el servidor alcanza los límites ReadHeaderTimeout/WriteTimeout.
Frameworks como chi, gin y echo exponen el mismo contexto de solicitud a través de sus APIs de manejador.
google.golang.org/grpc transmite metadatos y plazos a través de context.Context en cada RPC.
Los métodos database/sql que terminan en Context respetan la cancelación a nivel de driver cuando se admite.
Las rutas de apagado derivan un contexto de tiempo de espera de context.Background():
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
srv.Shutdown(ctx)| Mecanismo | Qué señala | Fuente típica |
|---|---|---|
WithCancel | Parada manual | Salida del manejador, fallo de errgroup |
WithTimeout | Presupuesto relativo | Llamada al cliente por RPC |
WithDeadline | Hora de finalización absoluta | Cabecera grpc-timeout de upstream |
Request.Context() | Desconexión del cliente | net/http |
context.WithoutCancel | Desvincular para limpieza | Registro posterior a la respuesta (Go 1.21+) |
Los servicios de producción apilan plazos en cada salto.
Una pasarela de borde podría permitir 5s; un servicio interno obtiene 3s después de la sobrecarga del middleware; una consulta de base de datos obtiene 500ms.
Cada capa debe establecer un plazo más corto que su padre para que haya margen para la serialización y las reintentos.
Los reconciliadores de controller-runtime usan contexto para detener el trabajo cuando un gestor se apaga o se pierde un arrendamiento.
Los controladores generados por kubebuilder pasan ctx a las llamadas al cliente para que las observaciones del servidor API respeten la cancelación.
golangci-lint incluye comprobaciones (a través de staticcheck y linters personalizados) para el uso indebido del contexto: pasar context.Background() dentro de los manejadores, almacenar contexto en structs o ignorar ctx en bucles.
Para los valores, el equipo de Go recomienda tipos de clave no exportados para evitar colisiones:
type ctxKey int
const requestIDKey ctxKey = 1Nunca uses valores de contexto para parámetros opcionales que pertenecen a los argumentos de la función.
Empareja el contexto con la observabilidad: extrae los IDs de solicitud del contexto en el middleware y adjúntalos a registros y trazas estructuradas.
| Enfoque | Fortaleza | Debilidad | Mejor ajuste |
|---|---|---|---|
| Cancelación de contexto | Estándar, componible | Requiere disciplina en cada llamada | Pilas HTTP/gRPC/DB |
time.After por llamada | Timeout local simple | Sin propagación a hijos | Scripts de goroutine única |
Canal done manual | Ciclo de vida personalizado | Reinvención del contexto | Rutas de código heredadas |
| Solo señales de proceso | Parada a nivel de SO | Sin granularidad por solicitud | Herramientas CLI |
errgroup + contexto | Cancela hermanos ante error | Necesita cableado explícito del grupo | Fan-out paralelo |
r.Context().cancel() de un hijo detiene a los descendientes, no a los ancestros.La convención de la comunidad y las herramientas gofmt/review esperan ctx context.Context primero.
Mantiene la señal visible en cada sitio de llamada.
WithCancel se detiene cuando llamas a cancel() o cuando el padre termina.
WithTimeout también establece un plazo y llama a cancel automáticamente cuando expira el tiempo.
Sí para WithCancel, WithTimeout y WithDeadline.
cancel() diferido libera recursos del temporizador incluso si el plazo expiró temprano.
Devuelve ctx.Err() sin cambios cuando la cancelación causó el fallo.
Envuelve con %w solo si los llamadores necesitan contexto adicional preservando errors.Is.
Métodos como QueryContext pasan la cancelación al driver cuando se admite.
Siempre prefiere las variantes *Context sobre las llamadas bloqueantes sin contexto.
Los plazos y metadatos viajan sobre context.Context.
Los stubs del cliente derivan tiempos de espera del contexto; los servidores los leen para su aplicación.
Nunca - usa context.Background() o context.TODO() solo en las raíces.
Las bibliotecas deben documentar que nil provoca un pánico o es rechazado.
Un marcador de posición cuando el contexto padre correcto no está claro durante las refactorizaciones.
Reemplázalo con un padre real antes de enviarlo.
Usa tipos de clave personalizados no exportados en cada paquete.
Las claves de cadena son un riesgo de colisión en bases de código grandes.
No - errgroup usa contexto para cancelar goroutines hermanas ante el primer error.
Se complementan entre sí.
Versiones de la pila: Esta página fue escrita para Go 1.26.x (predeterminado de GC Green Tea, go fix modernizers - verificar parche en la compilación), chi (última versión - verificar en la compilación), gin (última versión - verificar en la compilación), echo (última versión - verificar en la compilación), google.golang.org/grpc (última versión - verificar en la compilación), sigs.k8s.io/controller-runtime (última versión - verificar en la compilación), kubebuilder (última versión - verificar en la compilación), tinygo (última versión - verificar en la compilación), wazero (última versión - verificar en la compilación) y golangci-lint (última versión - verificar conjunto de linters en la compilación).
Revisado por Chris St. John·Última actualización: 19 jul 2026