Cloud Scheduler incluye encabezados HTTP de identificación de trabajos y de hora de programación en las solicitudes HTTP y de App Engine que te permiten anular el duplicado de las ejecuciones reintentadas y hacer que tus controladores de destino sean idempotentes. Debido a que Cloud Scheduler proporciona una entrega al menos una vez, la anulación de duplicación de solicitudes evita que tu controlador o las APIs de Google Cloud descendentes procesen la misma ejecución programada más de una vez cuando se producen tiempos de espera o interrupciones de red temporales.
Cómo funciona la anulación de duplicación de solicitudes
Las solicitudes de trabajos duplicadas para la misma ejecución programada pueden ocurrir en las siguientes situaciones:
- Destinos de larga duración: El servicio de destino tarda más en responder que la
fecha límite de intento del trabajo (
attemptDeadline), lo que hace que Cloud Scheduler agote el tiempo de espera y vuelva a intentar el trabajo mientras el primer intento aún se está ejecutando. Para los destinos HTTP,attemptDeadlinese establece de forma predeterminada en 3 minutos y se puede configurar hasta 30 minutos. AlineaattemptDeadlinecon el tiempo de espera de tu destino o procesa el trabajo de larga duración de forma asíncrona si la ejecución puede superar los 30 minutos. - Confirmaciones de red perdidas o reintentos de proxy transitorios: El destino
recibe y procesa la solicitud, pero una interrupción de red transitoria o
una desconexión del proxy impiden que Cloud Scheduler reciba la respuesta HTTP
2xx. Incluso si configurasretryCountcomo0, la entrega de al menos una vez significa que un destino aún puede recibir solicitudes duplicadas durante fallas de transporte temporales.
Para que el controlador de trabajos sea idempotente y se anule la duplicación de las solicitudes reintentadas, usa los encabezados HTTP que Cloud Scheduler incluye automáticamente en las solicitudes a los destinos HTTP y de App Engine:
| Encabezado | Descripción |
|---|---|
X-CloudScheduler-JobName |
Es el identificador corto del trabajo (JOB_ID) de
projects/PROJECT_ID/locations/LOCATION_ID/jobs/JOB_ID. |
X-CloudScheduler-ScheduleTime |
Para los trabajos especificados en
formato No se garantiza este encabezado para los trabajos que usan el
formato de programa alternativo de |
Exige autenticación en tu extremo de destino
Los encabezados X-CloudScheduler-* no se autentican y cualquier emisor puede
configurarlos. No uses estos encabezados para la autenticación. En su lugar, protege tu
extremo según el tipo de destino:
- Destinos HTTP: Configura el trabajo para que envíe un token de OIDC o de OAuth, y requiera autenticación en tu extremo. Para obtener más información, consulta Usa la autenticación con destinos de HTTP.
- Destinos de App Engine: Restringe el acceso al controlador configurando
login: adminen tu archivoapp.yamly configurando las reglas de firewall de App Engine.
Usa búsquedas de encabezados que no distingan mayúsculas de minúsculas
El uso de mayúsculas en los encabezados puede variar según las versiones de HTTP y la infraestructura (por
ejemplo, X-Cloudscheduler-Jobname en HTTP/1.1 o minúsculas
x-cloudscheduler-jobname y x-cloudscheduler-scheduletime en entornos HTTP/2,
como Cloud Run). Usa siempre búsquedas de encabezados que no distingan mayúsculas de minúsculas en tu controlador.
Elimina la ambigüedad de los IDs de trabajos en proyectos o regiones
Cloud Scheduler no envía automáticamente encabezados de ID del proyecto ni de
región. Un solo extremo de controlador puede recibir solicitudes de varios proyectos
o regiones que comparten el mismo JOB_ID. En este caso,
pasa el ID del proyecto o la región en
HttpTarget.headers,
AppEngineHttpTarget.headers,
o la ruta de URL de destino o los parámetros de consulta, y, luego, incluye esos valores en tu
clave de anulación de duplicación.
Anula la duplicación de solicitudes en un controlador HTTP
Antes de implementar la anulación de duplicación de solicitudes en tu controlador, completa los siguientes requisitos previos de configuración:
- Configura el programa de tu trabajo en el formato
unix-cron(por ejemplo,*/5 * * * *en lugar deevery 5 minutes) para que Cloud Scheduler envíe el encabezadoX-CloudScheduler-ScheduleTime. - Configura un almacén de datos persistente que admita transacciones o escrituras condicionales
atómicas, como Firestore,
Spanner o Memorystore.
Configura una política de tiempo de actividad (TTL) en la
colección o tabla (por ejemplo, en el campo
expire_at) para borrar los registros de anulación de duplicación antiguos después de la ventana de reintento del trabajo (maxRetryDuration). El TTL de Firestore borra los registros vencidos de forma asíncrona, por lo general, en un plazo de 24 a 72 horas. Para permitir que los reintentos reclamen ejecuciones de instancias con fallas antes de la limpieza del TTL, almacena también una marca de tiempo de vencimiento de la asignación (comolease_expires, establecida en, al menos, el tiempo de espera de la solicitud del destino yattemptDeadline) y evalúala en una transacción.
Para procesar cada ejecución programada solo una vez en tu controlador HTTP, completa los siguientes pasos:
- Lee
X-CloudScheduler-JobNameyX-CloudScheduler-ScheduleTimede los encabezados de la solicitud entrante y genera un hash para convertirlos en una clave de anulación de duplicación de longitud fija (run_id). - Ejecuta una transacción en tu almacén de datos para verificar y reclamar el registro de la
clave de anulación de duplicación:
- Si el registro existe y
statusescompleted, devuelve un código de estado HTTP200 OK(o204 No Content) de inmediato sin ejecutar la lógica del trabajo para que Cloud Scheduler deje de reintentar. - Si el registro existe,
statusesin_progressy el arrendamiento (lease_expires) no venció, devuelve un código de estado de error que se pueda reintentar, como409 Conflict, para que Cloud Scheduler mantenga activa su política de reintentos en caso de que falle el intento en curso. Evita devolver503 Service Unavailablede los destinos de App Engine, ya que App Engine trata503como un error del sistema y vuelve a intentar la solicitud sin contarla enretryCount. - Si el registro no existe o su arrendamiento venció, escribe el registro con
statusestablecido enin_progress, un ID de intento único (attempt_id), una marca de tiempo de vencimiento del arrendamiento (lease_expires) y una marca de tiempo de vencimiento del TTL (expire_at).
- Si el registro existe y
Después de reclamar el registro, ejecuta la lógica del trabajo. En una transacción, verifica que el
attempt_iddel registro aún coincida con el intento actual antes de modificar el registro:- Si la lógica del trabajo se ejecuta de forma correcta, actualiza
statusacompletedy devuelve un código de estado HTTP200 OK. - Si la lógica del trabajo falla con un error que puedes reintentar, borra el
registro de anulación de duplicación antes de devolver un código de estado
5xxpara que Cloud Scheduler pueda reintentar la ejecución sin esperar a que venza el arrendamiento.
- Si la lógica del trabajo se ejecuta de forma correcta, actualiza
Ejemplo de controlador HTTP
En el siguiente ejemplo de Python (para Cloud Run o Cloud Run Functions), se usa una transacción de Firestore con un arrendamiento para reclamar ejecuciones y anular la duplicación de solicitudes de trabajos reintentados.
Esta muestra no está lista para producción. Revísalo y ajústalo a tus necesidades antes de implementarlo:
Evita solicitudes duplicadas a destinos de la API de Google Cloud
Cuando Cloud Scheduler invoca un extremo de la API de Google Cloud
(*.googleapis.com) directamente a través de HTTP, la API de destino ignora los
encabezados X-CloudScheduler-JobName y X-CloudScheduler-ScheduleTime. Por
ejemplo, llamar a workflowexecutions.googleapis.com para iniciar una
ejecución de Workflows,
run.googleapis.com para ejecutar un
trabajo de Cloud Run o
cloudbuild.googleapis.com para ejecutar un
activador de compilación de Cloud Build inicia una ejecución nueva en
cada intento de reintento.
Para garantizar la confiabilidad de la producción, aplica la idempotencia en el servicio más descendente que modifica el estado. La anulación de duplicación de solicitudes solo en una capa upstream reduce el período de reintentos, pero si una solicitud downstream se realiza correctamente y la respuesta HTTP se pierde en la red, el emisor upstream no puede determinar si la operación se completó y es posible que vuelva a intentar la solicitud.
Para evitar ejecuciones duplicadas cuando programes destinos de la API de Google Cloud , usa los siguientes enfoques en conjunto para lograr la idempotencia de extremo a extremo:
Enruta las solicitudes a través de un proxy idempotente: En lugar de llamar al extremo de
*.googleapis.comdirectamente desde Cloud Scheduler, implementa tu propio servicio de Cloud Run o proxy de Cloud Run Functions y configura Cloud Scheduler para que invoque ese proxy. En el proxy, implementa el patrón de reclamación transaccional atómico de Python y Firestore que se describe en este documento, en Anula el duplicado de solicitudes en un controlador HTTP.El proxy lee
X-CloudScheduler-JobNameyX-CloudScheduler-ScheduleTime, quita los duplicados de las solicitudes reintentadas y pasa la clave de anulación de duplicación como un token de idempotencia en la carga útil de la solicitud (por ejemplo, en el campoargumentcuando se inicia una ejecución de Workflows).Exige la autenticación en el proxy (por ejemplo, implementa con
--no-allow-unauthenticated) y configura el trabajo de Cloud Scheduler para autenticar con un token de OIDC. Ejecuta el proxy en una cuenta de servicio dedicada con solo los roles de Identity and Access Management (IAM) mínimos requeridos (comoroles/workflows.invokeren el flujo de trabajo de destino yroles/datastore.userpara los registros de anulación de duplicación).Aplica la idempotencia en los servicios downstream: Usa el token de idempotencia para realizar una verificación atómica o transaccional en un almacén de datos, como Firestore, antes de ejecutar los pasos que cambian el estado. Pasa el token a cualquier extremo HTTP o función de Cloud Run downstream que invoque tu servicio para que esos destinos también puedan anular la duplicación de los reintentos. Para ver un ejemplo de extremo a extremo con Workflows, transacciones de Firestore y Cloud Run Functions, consulta la entrada de blog de Google Cloud, Realiza llamadas a la API exactamente una vez cuando usas Workflows.