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 reintento, hay dos factores que determinan si una solicitud es segura o no:

  1. La respuesta que recibes de la solicitud
  2. La idempotencia de la solicitud.

Consulta Implementa una estrategia de reintento para ver un análisis de estos factores y el algoritmo de reintento recomendado que se usará.

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 Respuesta 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.

Bibliotecas cliente

C++

Comportamiento de reintentos predeterminado

De forma predeterminada, las operaciones admiten reintentos para los siguientes códigos de error HTTP, así como cualquier error de socket que indique que se perdió la conexión o que nunca se estableció de forma correcta.

  • 408 Request Timeout
  • 429 Too Many Requests
  • 500 Internal Server Error
  • 502 Bad Gateway
  • 503 Service Unavailable
  • 504 Gateway Timeout

Todas las opciones de retirada exponencial y de reintentos en la biblioteca de C++ se pueden configurar. Si los algoritmos implementados en la biblioteca no satisfacen tus necesidades, puedes proporcionar un código personalizado para implementar tus propias estrategias.

Configuración Valor predeterminado
Reintento automático True
Tiempo máximo de reintentos de una solicitud 15 minutos
Tiempo de espera (retirada) inicial 1 segundo
Multiplicador de tiempo de espera por iteración 2
Tiempo máximo de espera 5 minutos

Según la configuración predeterminada, la biblioteca de C++ reintenta todas las operaciones con errores que se pueden reintentar, incluso aquellas que nunca son idempotentes y pueden borrar o crear varios recursos cuando se realizan con éxito de forma repetida. Para reintentar solo las operaciones idempotentes, usa google::cloud::storage::StrictIdempotencyPolicy.

Personaliza los reintentos

Para personalizar el comportamiento de reintento, proporciona valores para las siguientes opciones cuando inicialices el objeto google::cloud::storage::Client:

  • google::cloud::storage::RetryPolicyOption: La biblioteca proporciona las clases google::cloud::storage::LimitedErrorCountRetryPolicy y google::cloud::storage::LimitedTimeRetryPolicy. Puedes proporcionar tu propia clase, que debe implementar la interfaz google::cloud::RetryPolicy.

  • google::cloud::storage::BackoffPolicyOption: La biblioteca proporciona la clase google::cloud::storage::ExponentialBackoffPolicy. Puedes proporcionar tu propia clase, que debe implementar la interfaz google::cloud::storage::BackoffPolicy.

  • google::cloud::storage::IdempotencyPolicyOption: La biblioteca proporciona las clases google::cloud::storage::StrictIdempotencyPolicy y google::cloud::storage::AlwaysRetryIdempotencyPolicy. Puedes proporcionar tu propia clase, que debe implementar la interfaz google::cloud::storage::IdempotencyPolicy.

namespace gcs = ::google::cloud::storage;
// Create the client configuration:
auto options = google::cloud::Options{};
// Retries only idempotent operations.
options.set<gcs::IdempotencyPolicyOption>(
    gcs::StrictIdempotencyPolicy().clone());
// On error, it backs off for 1 second, then 3 seconds, then 9 seconds, etc.
// The backoff time never grows larger than 1 minute. The strategy introduces
// jitter around the backoff delay.
options.set<gcs::BackoffPolicyOption>(
    gcs::ExponentialBackoffPolicy(
        /*initial_delay=*/std::chrono::seconds(1),
        /*maximum_delay=*/std::chrono::minutes(1),
        /*scaling=*/3.0)
        .clone());
// Retries all operations for up to 5 minutes, including any backoff time.
options.set<gcs::RetryPolicyOption>(
    gcs::LimitedTimeRetryPolicy(std::chrono::minutes(5)).clone());
return gcs::Client(std::move(options));

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

Comportamiento de reintentos predeterminado

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

  • Errores de conexión
    • EAI_again: Este es un error de búsqueda de DNS. Puedes obtener más información en este vínculo.
    • Connection reset by peer: Esto significa que GCP restableció la conexión.
    • Unexpected connection closure: Esto significa que GCP cerró la conexión.
  • Códigos HTTP:
    • 408 Request Timeout
    • 429 Too Many Requests
    • 500 Internal Server Error
    • 502 Bad Gateway
    • 503 Service Unavailable
    • 504 Gateway Timeout

Toda la configuración de retirada exponencial de la biblioteca Node.js se puede configurar. De forma predeterminada, las operaciones a través de Node.js usan la siguiente configuración para la retirada exponencial:

Configuración Valor predeterminado (en segundos)
Reintento automático True
Cantidad máxima de reintentos 3
Tiempo de espera inicial 1 segundo
Multiplicador de tiempo de espera por iteración 2
Tiempo máximo de espera 64 segundos
Plazo predeterminado 600 segundos

Hay un subconjunto de las operaciones Node.js que son idempotentes de forma condicional (condicionalmente seguro para reintentar). Estas operaciones solo se reintentan si incluyen argumentos específicos, también conocidos como condiciones previas:

  • ifGenerationMatch o generation

    • Es seguro reintentarlo si ifGenerationMatch o generation se aprobaron como opción del método. A menudo, los métodos solo aceptan uno de estos dos parámetros.
  • ifMetagenerationMatch

    • Es seguro volver a intentarlo si ifMetagenerationMatch se pasó como una opción.

retryOptions.idempotencyStrategy se establece en IdempotencyStrategy.RetryConditional de forma predeterminada. Consulta la sección Personaliza reintentos a continuación a fin de obtener ejemplos para modificar el comportamiento de reintento predeterminado.

Personaliza los reintentos

Cuando inicializas Cloud Storage, también se inicializa un archivo de configuración retryOptions. A menos que se anulen, las opciones en la configuración se establecen en los valores de la tabla anterior. Para modificar el comportamiento de reintento predeterminado, pasa la configuración de reintento personalizada retryOptions al constructor de almacenamiento durante la inicialización. La biblioteca cliente de Node.js puede usar estrategias de retirada de forma automática para reintentar solicitudes con el parámetro autoRetry.

Consulta la siguiente muestra de código para aprender cómo personalizar tu comportamiento de reintento.

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// The ID of your GCS bucket
// const bucketName = 'your-unique-bucket-name';

// The ID of your GCS file
// const fileName = 'your-file-name';

// Imports the Google Cloud client library
const {Storage} = require('@google-cloud/storage');

// Creates a client
const storage = new Storage({
  retryOptions: {
    // If this is false, requests will not retry and the parameters
    // below will not affect retry behavior.
    autoRetry: true,
    // The multiplier by which to increase the delay time between the
    // completion of failed requests, and the initiation of the subsequent
    // retrying request.
    retryDelayMultiplier: 3,
    // The total time between an initial request getting sent and its timeout.
    // After timeout, an error will be returned regardless of any retry attempts
    // made during this time period.
    totalTimeout: 500,
    // The maximum delay time between requests. When this value is reached,
    // retryDelayMultiplier will no longer be used to increase delay time.
    maxRetryDelay: 60,
    // The maximum number of automatic retries attempted before returning
    // the error.
    maxRetries: 5,
    // Will respect other retry settings and attempt to always retry
    // conditionally idempotent operations, regardless of precondition
    idempotencyStrategy: IdempotencyStrategy.RetryAlways,
  },
});
console.log(
  'Functions are customized to be retried according to the following parameters:'
);
console.log(`Auto Retry: ${storage.retryOptions.autoRetry}`);
console.log(
  `Retry delay multiplier: ${storage.retryOptions.retryDelayMultiplier}`
);
console.log(`Total timeout: ${storage.retryOptions.totalTimeout}`);
console.log(`Maximum retry delay: ${storage.retryOptions.maxRetryDelay}`);
console.log(`Maximum retries: ${storage.retryOptions.maxRetries}`);
console.log(
  `Idempotency strategy: ${storage.retryOptions.idempotencyStrategy}`
);

async function deleteFileWithCustomizedRetrySetting() {
  await storage.bucket(bucketName).file(fileName).delete();
  console.log(`File ${fileName} deleted with a customized retry strategy.`);
}

deleteFileWithCustomizedRetrySetting();

PHP

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

Python

Comportamiento de reintentos predeterminado

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)
    • ConnectionError
  • Códigos HTTP:
    • 408 Request Timeout
    • 429 Too Many Requests
    • 500 Internal Server Error
    • 502 Bad Gateway
    • 503 Service Unavailable
    • 504 Gateway Timeout

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

Existe un subconjunto de operaciones de Python que son idempotentes de forma condicional (condicionalmente seguras para reintentar) cuando incluyen argumentos específicos. Estas operaciones solo se reintentan si se aprueba un caso de condición:

  • 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.

Personaliza los reintentos

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. La biblioteca cliente de Python usa estrategias de retirada de forma automática para reintentar las solicitudes si incluyes el parámetro DEFAULT_RETRY.

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.

Consulta la siguiente muestra de código para aprender cómo personalizar tu comportamiento de reintento.

from google.cloud import storage
from google.cloud.storage.retry import DEFAULT_RETRY

def configure_retries(bucket_name, blob_name):
    """Configures retries with customizations."""
    # The ID of your GCS bucket
    # bucket_name = "your-bucket-name"
    # The ID of your GCS object
    # blob_name = "your-object-name"

    storage_client = storage.Client()
    bucket = storage_client.bucket(bucket_name)
    blob = bucket.blob(blob_name)

    # Customize retry with a deadline of 500 seconds (default=120 seconds).
    modified_retry = DEFAULT_RETRY.with_deadline(500.0)
    # Customize retry with an initial wait time of 1.5 (default=1.0).
    # Customize retry with a wait time multiplier per iteration of 1.2 (default=2.0).
    # Customize retry with a maximum wait time of 45.0 (default=60.0).
    modified_retry = modified_retry.with_delay(initial=1.5, multiplier=1.2, maximum=45.0)

    # blob.delete() uses DEFAULT_RETRY_IF_GENERATION_SPECIFIED by default.
    # Override with modified_retry so the function retries even if the generation
    # number is not specified.
    print(
        f"The following library method is customized to be retried according to the following configurations: {modified_retry}"
    )

    blob.delete(retry=modified_retry)
    print("Blob {} deleted with a customized retry strategy.".format(blob_name))

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.

Implementa una estrategia de reintento

En esta sección se analizan los factores que se deben tener en cuenta cuando se compila tu propia estrategia de reintento, así como el algoritmo de reintento recomendado para usar.

Respuesta

La respuesta que recibes de tu solicitud indica si es útil reintentar la solicitud. Por lo general, las respuestas relacionadas con los problemas transitorios se pueden reintentar. Por otro lado, la respuesta relacionada con errores permanentes indica que debes realizar cambios, como cambios de autorización o configuración, antes de que sea útil reintentar la solicitud. Las siguientes respuestas indican problemas transitorios que son útiles para reintentar:

  • Códigos de respuesta HTTP 4084295xx.
  • 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.

Idempotencia

Una solicitud que es idempotente significa que se puede realizar de manera reiterada y que siempre deje el recurso al que se orienta en el mismo estado final. Por ejemplo, las solicitudes de listas siempre son idempotentes, ya que estas solicitudes no modifican recursos. Por otro lado, crear una notificación de Pub/Sub nueva nunca es idempotente, ya que crea un ID de notificación nuevo cada vez que la solicitud se realiza de forma correcta.

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.

Cuando recibes una respuesta que se puede reintentar, debes considerar la idempotencia de la solicitud, ya que reintentar solicitudes que no son idempotentes puede generar condiciones de carrera y otros conflictos.

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.

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

Algoritmo de retirada exponencial

Por lo general, en las solicitudes que cumplen con los criterios de respuesta y de idempotencia, debes usar la retirada exponencial truncada.

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.

¿Qué sigue?