Estrategia de reintentos

En esta página, se describen las estrategias de reintentos, como la retirada exponencial truncada para las solicitudes fallidas a Cloud Storage.

Descripción general

Muchas herramientas de Cloud Storage, como Cloud Console y la mayoría de las bibliotecas cliente, de forma automática usan una estrategia de reintentos, por lo que, generalmente, no necesitas implementar la tuya. Si implementas tu propia estrategia de reintentos, considera el tipo de solicitud y su idempotencia, que determinan si la solicitud es segura para reintentarla. En el caso de las solicitudes que sean seguras para reintentarse, en general, debes usar la retirada exponencial truncada. Las siguientes son solicitudes que se pueden reintentar:

  • Todas las solicitudes a Cloud Storage que muestran códigos de respuesta HTTP 5xx, incluidas las cargas y descargas de datos o metadatos.

  • Solicitudes a Cloud Storage que muestran un código de respuesta HTTP 408

  • Solicitudes a Cloud Storage que muestran un código de respuesta HTTP 429, excepto las solicitudes que suben datos mediante cargas reanudables.

  • Tiempos de espera de sockets agotados y desconexiones de TCP

Si quieres obtener más información, consulta los códigos de estado y error para JSON y XML.

Algoritmo de retirada exponencial

La retirada exponencial truncada es una estrategia estándar de manejo de errores para aplicaciones de red en la que el cliente vuelve a intentar de forma periódica una solicitud con errores, cada vez con más frecuencia entre las solicitudes.

Un algoritmo de retirada exponencial vuelve a intentar las solicitudes de forma exponencial, lo que aumenta el tiempo de espera entre los reintentos hasta un tiempo de retirada máximo. A continuación, se presenta un ejemplo:

  1. Haz una solicitud a Cloud Storage.

  2. Si la solicitud falla, espera 1 + random_number_milliseconds segundos y vuelve a intentar la solicitud.

  3. Si la solicitud falla, espera 2 + random_number_milliseconds segundos y vuelve a intentar la solicitud.

  4. Si la solicitud falla, espera 4 + random_number_milliseconds segundos y vuelve a intentar la solicitud.

  5. Y así sucesivamente, hasta un tiempo de maximum_backoff.

  6. Continúa con la espera y los reintentos hasta una cantidad de tiempo máxima (deadline), pero no aumentes el período de espera de maximum_backoff entre los reintentos.

Donde:

  • El tiempo de espera es min((2n +random_number_milliseconds), maximum_backoff), con n incrementado en 1 para cada iteración (solicitud).

  • random_number_milliseconds es un número al azar de milisegundos menor o igual que 1,000. Esto ayuda a evitar los casos en los que muchos clientes se sincronizan y se vuelven a intentar a la vez, lo que hace que se envíen solicitudes sincronizadas en etapas. El valor de random_number_milliseconds se vuelve a calcular después de cada reintento de solicitud.

  • maximum_backoff suele ser de 32 o 64 segundos. El valor apropiado depende del caso práctico.

Puedes continuar con los reintentos una vez que alcances el tiempo maximum_backoff, pero recomendamos que tu solicitud falle después de un período para evitar que tu aplicación no responda. Por ejemplo, si un cliente usa un tiempo maximum_backoff de 64 segundos, luego de alcanzar este valor, puede volver a intentarlo cada 64 segundos. Luego, el cliente deja de reintentarlo después de un deadline de 600 segundos.

El tiempo que los clientes deben esperar entre los reintentos y la cantidad de reintentos que deben realizar dependen de tu caso práctico y las condiciones de la red. Por ejemplo, es posible que los clientes móviles de una aplicación deban intentar más veces y por intervalos más largos, en comparación con los clientes de escritorio de la misma aplicación.

Si las solicitudes de reintento fallan después de exceder el maximum_backoff y cualquier tiempo adicional permitido para los reintentos, informa o registra un error mediante uno de los métodos enumerados en Asistencia y ayuda.

Idempotencia

A fin de determinar si es seguro reintentar una solicitud con errores en Cloud Storage, considera si la solicitud idempotente, lo que significa que la aplicación de la misma operación varias veces tiene el mismo efecto sobre el estado del recurso de destino. Generalmente, se puede reintentar ejecutar las operaciones de idempotente de forma segura.

Los siguientes son ejemplos de condiciones que satisfacen la idempotencia:

  • La operación tiene el mismo efecto observable en el recurso objetivo incluso cuando se solicita de forma continua.

  • La operación solo se realiza una vez.

  • La operación no tiene un efecto observable en el estado del recurso de destino.

Por ejemplo, una solicitud para crear una lista de buckets tiene el mismo efecto, incluso si la solicitud es exitosa varias veces. Por otro lado, una operación como crear una nueva notificación de Pub/Sub no es idempotente, ya que crea un nuevo ID de notificación cada vez que la solicitud se realiza correctamente.

Idempotencia condicional

Un subconjunto de solicitudes es condicionalmente idempotente, lo que significa que solo son idempotentes si incluyen argumentos opcionales específicos. Las operaciones condicionalmente seguras para su reintento solo deben reintentarse de forma predeterminada si se aprueba el caso de la condición. Cloud Storage acepta condiciones previas y ETag como casos de condición para las solicitudes.

Estrategia de reintento por la herramienta de Cloud Storage

Haz clic en las pestañas que están a continuación para ver las recomendaciones de las estrategias de reintento de cada herramienta de Cloud Storage.

Console

Cloud Console envía solicitudes a Cloud Storage en tu nombre y controla las retiradas necesarias.

gsutil

Gsutil reintenta los errores enumerados en la sección Descripción general sin que debas realizar ninguna acción adicional. Es posible que debas tomar medidas en caso de otros errores, como los siguientes:

  • Credenciales no válidas o permisos insuficientes.

  • No se puede acceder a la red debido a un problema de configuración del proxy.

  • Operaciones individuales que fallan dentro de un comando en el que usas la marca de nivel superior -m.

Para los errores que se pueden intentar de nuevo, gsutil vuelve a intentar las solicitudes mediante una estrategia de retirada exponencial binaria truncada. De forma predeterminada, gsutil vuelve a intentarlo 23 veces en 1+2+4+8+16+32+60… segundos durante alrededor de 10 minutos:

  • Si una solicitud falla, espera un período aleatorio entre 0 y 1 segundo y vuelve a intentarlo.
  • Si la solicitud falla nuevamente, espera un período aleatorio entre [0..2] segundos y vuelve a intentarlo.
  • Si la solicitud falla nuevamente, espera un período aleatorio entre [0..4] segundos y vuelve a intentarlo.
  • Y así sucesivamente, hasta 23 reintentos, con cada período de reintento limitado por un máximo predeterminado de 60 segundos.

Para configurar la cantidad de reintentos y el retraso máximo de cualquier reintento individual, edita las variables de configuración num_retries y max_retry_delay en la sección "[Boto]" de archivo de configuración .boto.

Para las transferencias de datos que usan los comandos de cp y rsync de gsutil, este proporciona funcionalidad de reintento adicional en forma de transferencias reanudables.

Bibliotecas cliente

C++

La biblioteca cliente C++ usa la retirada exponencial de forma predeterminada.

C#

La biblioteca cliente C# usa la retirada exponencial de forma predeterminada.

Comienza a usarlo

La biblioteca cliente de Go usa la retirada exponencial de forma predeterminada.

Java

La biblioteca cliente de Java usa la retirada exponencial de forma predeterminada.

Node.js

La biblioteca cliente de Node.js puede usar estrategias de retirada de forma automática para reintentar solicitudes con el parámetro autoRetry.

PHP

La biblioteca cliente de PHP usa la retirada exponencial de forma predeterminada.

Python

De forma predeterminada, las operaciones admiten reintentos para los siguientes códigos de error:

  • Errores de conexión
    • requests.exceptions.ConnectionError
    • requests.exceptions.ChunkedEncodingError (solo para operaciones que recuperan o envían datos de carga útil a objetos, como cargas y descargas)
  • Códigos HTTP:
    • 429 Too Many Requests
    • 500 Internal Server Error
    • 502 Bad Gateway
    • 503 Service Unavailable
    • 504 Gateway Timeout
    • 508 Resource Limit Exceeded

Las operaciones a través de Python usan la siguiente configuración predeterminada para la retirada exponencial:

Configuración Valor predeterminado (en segundos)
Tiempo de espera inicial 1
Multiplicador de tiempo de espera por iteración 2
Tiempo máximo de espera 60
Plazo predeterminado 120

Un subconjunto de operaciones solo es idempotente si incluye argumentos opcionales específicos. Las operaciones condicionalmente seguras para su reintento solo deben reintentarse de forma predeterminada si se aprueba el caso de la condición. Actualmente, estas condiciones incluyen lo siguiente:

  • DEFAULT_RETRY_IF_GENERATION_SPECIFIED

    • Es seguro reintentarlo si generation o if_generation_match se aprobaron como argumento del método. A menudo, los métodos solo aceptan uno de estos dos parámetros.
  • DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED

    • Es seguro reintentarlo si if_metageneration_match se aprobó como argumento del método.
  • DEFAULT_RETRY_IF_ETAG_IN_JSON

    • Es seguro reintentarlo si el método inserta un etag en el cuerpo de la solicitud JSON. Para HMACKeyMetadata.update(), esto significa que la ETag se debe configurar en el objeto HMACKeyMetadata. Para el método set_iam_policy() en otras clases, significa que la ETag se debe establecer en el argumento de la “política” que se pasó al método.

Políticas de reintento

Las operaciones que son seguras o son condicionalmente seguras para reintentarse, tienen un parámetro retry agregado a la firma de su método. El valor predeterminado para estos parámetros es uno de los siguientes:

  • DEFAULT_RETRY
  • DEFAULT_RETRY_IF_GENERATION_SPECIFIED
  • DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED
  • DEFAULT_RETRY_IF_ETAG_IN_JSON

Para modificar el comportamiento de reintento predeterminado, crea una copia del objeto google.cloud.storage.retry.DEFAULT_RETRY llamándolo con un método with_XXX. Ten en cuenta que with_predicate no es compatible con operaciones que recuperan o envían datos de carga útil a objetos, como cargas y descargas. Te recomendamos modificar los atributos uno por uno. Para obtener más información, consulta la referencia Operaciones de reintento de google-api-core.

Para configurar tu propio reintento condicional, crea un objeto ConditionalRetryPolicy y une tu objeto Retry personalizado con DEFAULT_RETRY_IF_GENERATION_SPECIFIED, DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED o DEFAULT_RETRY_IF_ETAG_IN_JSON.

Los siguientes son ejemplos de reintentos condicionales personalizados:

  • blob.reload() usa DEFAULT_RETRY de forma predeterminada. A fin de anular esto para que la función no se vuelva a intentar, llámala como blob.reload(retry=None).

  • bucket.update() usa DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED de forma predeterminada. Para anular esto, de modo que la función se vuelva a intentar incluso si la cantidad de metageneración no se especifica, llámalo de la siguiente manera:

    from google.cloud.storage.retry import DEFAULT_RETRY
    bucket.update(retry=DEFAULT_RETRY)
  • bucket.list_blobs() usa DEFAULT_RETRY de forma predeterminada. Para anular esto, de modo que la llamada a la API se vuelva a intentar con un plazo de 20 segundos en lugar de los 120 segundos predeterminados, llámalo de la siguiente manera:

    from google.cloud.storage.retry import DEFAULT_RETRY
    modified_retry = DEFAULT_RETRY.with_deadline(20)
    bucket.list_blobs(retry=modified_retry)

Ruby

La biblioteca cliente de Ruby usa la retirada exponencial de forma predeterminada.

API de REST

Cuando llames a la API de XML o JSON directamente, debes usar el algoritmo de retirada exponencial para implementar tu propia estrategia de reintento.

Idempotencia de operaciones

En la siguiente tabla, se describen las operaciones de Cloud Storage que corresponden a cada categoría de idempotencia.

Idempotencia Operations
Siempre idempotente
  • Todas las solicitudes get y list
  • Inserta o borra buckets
  • Políticas y permisos de IAM del bucket de prueba
  • Bloquear políticas de retención
  • Borra una clave HMAC o una notificación de Pub/Sub
Condicionalmente idempotente
  • Solicitudes de actualización o parche para buckets con IfMetagenerationMatch o ETag como condición previa HTTP
  • Solicitudes de actualización o parche para objetos con IfMetagenerationMatch o ETag como condición previa HTTP
  • Establece una política de IAM del bucket con ETag como condición previa HTTP o en el cuerpo del recurso
  • Actualiza una clave HMAC con ETag como condición previa HTTP o en el cuerpo del recurso
  • Inserta, copia, redacta o reescribe objetos con ifGenerationMatch
  • Borra un objeto con ifGenerationMatch (o con un número de generación para versiones de objetos)
Nunca idempotente
  • Crea una clave HMAC
  • Crea una notificación de Pub/Sub
  • Crea, borra o envía solicitudes de parche o actualización para las LCA de buckets y objetos o las LCA de objeto predeterminadas

¿Qué sigue?