Reintentar estrategia

En esta página se describe cómo vuelven a intentar las herramientas de Cloud Storage las solicitudes fallidas y cómo personalizar el comportamiento de los reintentos. También se describen las consideraciones para volver a intentar las solicitudes.

Información general

Hay dos factores que determinan si una solicitud se puede volver a intentar de forma segura:

  • La respuesta que recibes de la solicitud.

  • La idempotencia de la solicitud.

Respuesta

La respuesta que recibas de tu solicitud indica si es útil o no volver a intentarlo. Las respuestas relacionadas con problemas transitorios se pueden volver a intentar. Por otro lado, las respuestas relacionadas con errores permanentes indican que debes hacer cambios, como cambios en la autorización o en la configuración, antes de que sea útil volver a intentar la solicitud. Las siguientes respuestas indican problemas transitorios que se pueden volver a intentar:

  • Códigos de respuesta HTTP 408, 429 y 5xx.
  • Tiempos de espera de sockets y desconexiones TCP.

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

Idempotencia

Las solicitudes idempotentes se pueden ejecutar repetidamente sin cambiar el estado final del recurso de destino, lo que da como resultado el mismo estado final cada vez. Por ejemplo, las operaciones de lista siempre son idempotentes, ya que estas solicitudes no modifican los recursos. Por otro lado, crear una notificación de Pub/Sub nunca es idempotente, ya que se crea un nuevo ID de notificación cada vez que la solicitud se completa correctamente.

A continuación se muestran ejemplos de condiciones que hacen que una operación sea idempotente:

  • La operación tiene el mismo efecto observable en el recurso de destino incluso cuando se solicita continuamente.

  • La operación solo se realiza correctamente una vez.

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

Cuando recibas una respuesta que se pueda volver a intentar, debes tener en cuenta la idempotencia de la solicitud, ya que volver a intentar solicitudes que no son idempotentes puede provocar condiciones de carrera y otros conflictos.

Idempotencia condicional

Un subconjunto de solicitudes son condicionalmente idempotentes, lo que significa que solo son idempotentes si incluyen argumentos opcionales específicos. Las operaciones que se pueden reintentar de forma segura solo se deben reintentar de forma predeterminada si se cumple la condición. Cloud Storage acepta condiciones previas y ETags como casos de condiciones para las solicitudes.

Idempotencia de las operaciones

En la siguiente tabla se enumeran las operaciones de Cloud Storage que se incluyen en cada categoría de idempotencia.

Idempotencia Operaciones
Siempre idempotente
  • Todas las solicitudes de obtención y de lista
  • Insertar o eliminar contenedores
  • Probar políticas y permisos de gestión de identidades y accesos de un segmento
  • Bloquear políticas de retención
  • Eliminar una clave HMAC o una notificación de Pub/Sub
Idempotente condicionalmente
  • Solicitudes de actualización o parche para los segmentos con IfMetagenerationMatch1 o etag1 como condición previa HTTP
  • Solicitudes de actualización o parche de objetos con IfMetagenerationMatch1 o etag1 como condición previa HTTP
  • Definir una política de gestión de identidades y accesos de un segmento con etag1 como condición previa HTTP o en el cuerpo del recurso
  • Actualizar una clave HMAC con etag1 como condición previa HTTP o en el cuerpo del recurso
  • Insertar, copiar, redactar o reescribir objetos con ifGenerationMatch1
  • Eliminar un objeto con ifGenerationMatch1 (o con un número de generación para las versiones del objeto)
Nunca idempotente
  • Crear una clave HMAC
  • Crear una notificación de Pub/Sub
  • Crear, eliminar o enviar solicitudes de parche o actualización para las LCA de segmentos y objetos, o para las LCA de objetos predeterminadas

1 Este campo se puede usar en la API JSON. Para ver los campos que se pueden usar en las bibliotecas de cliente, consulta la documentación de la biblioteca de cliente correspondiente.

Cómo implementan las herramientas de Cloud Storage las estrategias de reintento

Consola

La consola Google Cloud envía solicitudes a Cloud Storage en tu nombre y gestiona cualquier retroceso necesario.

Línea de comandos

Los comandos gcloud storage vuelven a intentar los errores que se indican en la sección Respuesta sin que tengas que hacer nada más. Es posible que tengas que tomar medidas para corregir 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.

En el caso de los errores que se pueden volver a intentar, la CLI de gcloud vuelve a enviar las solicitudes mediante una estrategia de retirada exponencial binaria truncada. El número predeterminado de reintentos máximos es 32 para la CLI de gcloud.

Bibliotecas de cliente

C++

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

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

Todos los ajustes de retroceso exponencial y reintento de la biblioteca de C++ se pueden configurar. Si los algoritmos implementados en la biblioteca no se ajustan a tus necesidades, puedes proporcionar código personalizado para implementar tus propias estrategias.

Ajuste Valor predeterminado
Reintento automático Verdadero
Tiempo máximo para reintentar una solicitud 15 minutos
Tiempo de espera inicial (retirada) 1 segundo
Multiplicador del tiempo de espera por iteración 2
Tiempo máximo de espera 5 minutos

De forma predeterminada, la biblioteca de C++ vuelve a intentar todas las operaciones con errores que se pueden volver a intentar, incluso las que nunca son idempotentes y pueden eliminar o crear varios recursos cuando se realizan correctamente varias veces. Para volver a intentar solo las operaciones idempotentes, usa la clase google::cloud::storage::StrictIdempotencyPolicy.

C#

La biblioteca de cliente de C# usa el retroceso exponencial de forma predeterminada.

Go

De forma predeterminada, las operaciones admiten reintentos en caso de que se produzcan los siguientes errores:

  • Errores de conexión:
    • io.ErrUnexpectedEOF: esto puede ocurrir debido a problemas de red transitorios.
    • url.Error que contiene connection refused: esto puede ocurrir debido a problemas de red temporales.
    • url.Error que contiene connection reset by peer: Esto significa que Google Cloud ha restablecido la conexión.
    • net.ErrClosed: esto significa que Google Cloud ha cerrado 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
  • Errores que implementan la interfaz Temporary() y dan un valor de err.Temporary() == true
  • Cualquiera de los errores anteriores que se haya envuelto con envoltura de errores de Go 1.13

Todos los ajustes de retroceso exponencial de la biblioteca de Go se pueden configurar. De forma predeterminada, las operaciones de Go usan los siguientes ajustes para la retirada exponencial (los valores predeterminados se toman de gax):

Ajuste Valor predeterminado (en segundos)
Reintento automático True si es idempotente
Número máximo de intentos Sin límite
Retraso inicial de reintento 1 segundo
Multiplicador del retraso de reintento 2,0
Retraso máximo de reintento 30 segundos
Tiempo de espera total (fragmento de subida reanudable) 32 segundos
Tiempo de espera total (todas las demás operaciones) Sin límite

Por lo general, los reintentos continúan indefinidamente a menos que se cancele el contexto de control, se cierre el cliente o se reciba un error no transitorio. Para evitar que se sigan reintentando las operaciones, usa los tiempos de espera de contexto o la cancelación. La única excepción a este comportamiento se da cuando se realizan subidas reanudables con Writer, donde los datos son lo suficientemente grandes como para requerir varias solicitudes. En este caso, cada fragmento agota el tiempo de espera y deja de reintentarse después de 32 segundos de forma predeterminada. Puedes ajustar el tiempo de espera predeterminado cambiando Writer.ChunkRetryDeadline.

Hay un subconjunto de operaciones de Go que son condicionalmente idempotentes (se pueden reintentar de forma segura en determinadas condiciones). Estas operaciones solo se vuelven a intentar si cumplen condiciones específicas:

  • GenerationMatch o Generation

    • Se puede volver a intentar si se ha aplicado una condición previa GenerationMatch a la llamada o si se ha definido ObjectHandle.Generation.
  • MetagenerationMatch

    • Se puede volver a intentar si se ha aplicado una condición previa MetagenerationMatch a la llamada.
  • Etag

    • Se puede volver a intentar si el método inserta un etag en el cuerpo de la solicitud JSON. Solo se usa en HMACKeyHandle.Update cuando se ha definido HmacKeyMetadata.Etag.

RetryPolicy tiene el valor RetryPolicy.RetryIdempotent de forma predeterminada. Consulta Personalizar reintentos para ver ejemplos de cómo modificar el comportamiento predeterminado de los reintentos.

Java

De forma predeterminada, las operaciones admiten reintentos en caso de que se produzcan los siguientes errores:

  • Errores de conexión:
    • Connection reset by peer: esto significa que Google Cloud ha restablecido la conexión.
    • Unexpected connection closure: esto significa que Google Cloud ha cerrado 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

Todos los ajustes de retroceso exponencial de la biblioteca de Java se pueden configurar. De forma predeterminada, las operaciones a través de Java usan los siguientes ajustes para el tiempo de espera exponencial:

Ajuste Valor predeterminado (en segundos)
Reintento automático True si es idempotente
Número máximo de intentos 6
Retraso inicial de reintento 1 segundo
Multiplicador del retraso de reintento 2,0
Retraso máximo de reintento 32 segundos
Tiempo de espera total 50 segundos
Tiempo de espera inicial de RPC 50 segundos
Multiplicador de tiempo de espera de RPC 1.0
Tiempo de espera máximo de RPC 50 segundos
Tiempo de espera de conexión agotado 20 segundos
Tiempo de espera de lectura 20 segundos

Para obtener más información sobre los ajustes, consulta la documentación de referencia de Java sobre RetrySettings.Builder y HttpTransportOptions.Builder.

Hay un subconjunto de operaciones de Java que son condicionalmente idempotentes (se pueden reintentar de forma condicional). Estas operaciones solo se reintentan si incluyen argumentos específicos:

  • ifGenerationMatch o generation

    • Es seguro volver a intentarlo si se ha introducido ifGenerationMatch o generation como opción del método.
  • ifMetagenerationMatch

    • Es seguro volver a intentarlo si se ha pasado ifMetagenerationMatch como opción.

StorageOptions.setStorageRetryStrategy tiene el valor StorageRetryStrategy#getDefaultStorageRetryStrategy de forma predeterminada. Consulta Personalizar reintentos para ver ejemplos de cómo modificar el comportamiento de reintento predeterminado.

Node.js

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

  • Errores de conexión:
    • EAI_again: se trata de un error de petición de DNS. Para obtener más información, consulta la documentación de getaddrinfo.
    • Connection reset by peer: esto significa que Google Cloud ha restablecido la conexión.
    • Unexpected connection closure: esto significa que Google Cloud ha cerrado 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

Todos los ajustes de retroceso exponencial de la biblioteca Node.js se pueden configurar. De forma predeterminada, las operaciones a través de Node.js utilizan los siguientes ajustes para el tiempo de espera exponencial:

Ajuste Valor predeterminado (en segundos)
Reintento automático True si es idempotente
Número máximo de reintentos 3
Tiempo de espera inicial 1 segundo
Multiplicador del tiempo de espera por iteración 2
Tiempo máximo de espera 64 segundos
Fecha límite predeterminada 600 segundos

Hay un subconjunto de operaciones de Node.js que son condicionalmente idempotentes (es decir, se pueden reintentar de forma segura en determinadas condiciones). Estas operaciones solo se vuelven a intentar si incluyen argumentos específicos:

  • ifGenerationMatch o generation

    • Es seguro volver a intentarlo si se ha introducido ifGenerationMatch o generation 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 se ha pasado ifMetagenerationMatch como opción.

retryOptions.idempotencyStrategy tiene el valor IdempotencyStrategy.RetryConditional de forma predeterminada. Consulta Personalizar reintentos para ver ejemplos de cómo modificar el comportamiento predeterminado de los reintentos.

PHP

La biblioteca de cliente de PHP usa el retroceso exponencial de forma predeterminada.

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

  • Errores de conexión:
    • connetion-refused: puede deberse a problemas de red temporales.
    • connection-reset: esto significa que Google Cloud ha restablecido la conexión.
  • Códigos HTTP:
    • 200: para casos de descarga parcial
    • 408 Request Timeout
    • 429 Too Many Requests
    • 500 Internal Server Error
    • 502 Bad Gateway
    • 503 Service Unavailable
    • 504 Gateway Timeout

Algunos ajustes de retroceso exponencial de la biblioteca PHP se pueden configurar. De forma predeterminada, las operaciones a través de PHP usan los siguientes ajustes para el tiempo de espera exponencial:

Ajuste Valor predeterminado (en segundos)
Reintento automático True si es idempotente
Retraso inicial de reintento 1 segundo
Multiplicador del retraso de reintento 2,0
Retraso máximo de reintento 60 segundos
Tiempo de espera de solicitud 0 con REST y 60 con gRPC
Número de reintentos predeterminado 3

Hay un subconjunto de operaciones de PHP que son condicionalmente idempotentes (se pueden reintentar de forma segura en determinadas condiciones). Estas operaciones solo se vuelven a intentar si incluyen argumentos específicos:

  • ifGenerationMatch o generation

    • Es seguro volver a intentarlo si se ha introducido ifGenerationMatch o generation 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 se ha pasado ifMetagenerationMatch como opción.

Al crear StorageClient, se usa la estrategia StorageClient::RETRY_IDEMPOTENT de forma predeterminada. Consulta Personalizar reintentos para ver ejemplos de cómo modificar el comportamiento predeterminado de los reintentos.

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 obtienen o envían datos de carga útil a objetos, como las subidas y descargas)
    • ConnectionError
    • http.client.ResponseNotReady
    • urllib3.exceptions.TimeoutError
  • 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 los siguientes ajustes predeterminados para la retirada exponencial:

Ajuste Valor predeterminado (en segundos)
Reintento automático True si es idempotente
Tiempo de espera inicial 1
Multiplicador del tiempo de espera por iteración 2
Tiempo máximo de espera 60
Fecha límite predeterminada 120

Además de las operaciones de Cloud Storage que son siempre idempotentes, la biblioteca de cliente de Python vuelve a intentar automáticamente las operaciones Objects: insert, Objects: delete y Objects: patch de forma predeterminada.

Hay un subconjunto de operaciones de Python que son condicionalmente idempotentes (se pueden volver a intentar de forma segura) cuando incluyen argumentos específicos. Estas operaciones solo se vuelven a intentar si se cumple un caso de condición:

  • DEFAULT_RETRY_IF_GENERATION_SPECIFIED

    • Se puede volver a intentar si se ha pasado generation o if_generation_match como argumento al método. A menudo, los métodos solo aceptan uno de estos dos parámetros.
  • DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED

    • Se puede volver a intentar si if_metageneration_match se ha enviado como argumento al método.
  • DEFAULT_RETRY_IF_ETAG_IN_JSON

    • Se puede volver a intentar si el método inserta un etag en el cuerpo de la solicitud JSON. En el caso de HMACKeyMetadata.update(), esto significa que el etag debe definirse en el propio objeto HMACKeyMetadata. En el caso del método set_iam_policy() de otras clases, esto significa que el etag debe definirse en el argumento "policy" que se pasa al método.

Ruby

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

  • Errores de conexión:
    • SocketError
    • HTTPClient::TimeoutError
    • Errno::ECONNREFUSED
    • HTTPClient::KeepAliveDisconnected
  • Códigos HTTP:
    • 408 Request Timeout
    • 429 Too Many Requests
    • 5xx Server Error

Todos los ajustes de retroceso exponencial de la biblioteca de cliente de Ruby se pueden configurar. De forma predeterminada, las operaciones a través de la biblioteca de cliente de Ruby usan los siguientes ajustes para la retención exponencial:

Ajuste Valor predeterminado
Reintento automático Verdadero
Número máximo de reintentos 3
Tiempo de espera inicial 1 segundo
Multiplicador del tiempo de espera por iteración 2
Tiempo máximo de espera 60 segundos
Fecha límite predeterminada 900 segundos

Hay un subconjunto de operaciones de Ruby que son condicionalmente idempotentes (es decir, se pueden volver a intentar de forma segura) cuando incluyen argumentos específicos:

  • if_generation_match o generation

    • Se puede volver a intentar si se pasa el parámetro generation o if_generation_match como argumento al método. A menudo, los métodos solo aceptan uno de estos dos parámetros.
  • if_metageneration_match

    • Se puede volver a intentar si el parámetro if_metageneration_match se pasa como una opción.

De forma predeterminada, se vuelve a intentar realizar todas las operaciones idempotentes y las operaciones idempotentes condicionales solo si se cumple la condición. Las operaciones no idempotentes no se vuelven a intentar. Consulta Personalizar reintentos para ver ejemplos de cómo modificar el comportamiento predeterminado de los reintentos.

APIs REST

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

Personalizar reintentos

Consola

No puedes personalizar el comportamiento de los reintentos mediante la Google Cloud consola.

Línea de comandos

En el caso de los comandos gcloud storage, puede controlar la estrategia de reintento creando una configuración con nombre y definiendo algunas o todas las propiedades siguientes:

Ajuste Valor predeterminado (en segundos)
base_retry_delay 1
exponential_sleep_multiplier 2
max_retries 32
max_retry_delay 32

A continuación, aplica la configuración definida por comando mediante la marca --configuration de todo el proyecto o para todos los comandos de la CLI de Google Cloud mediante el comando gcloud config set.

Bibliotecas de cliente

C++

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.

Para obtener más información, consulta la documentación de referencia de la biblioteca de cliente de C++.

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 a random delay between [1, 3] seconds, then [3,
// 9] seconds, then [9, 27] seconds, etc. The backoff time never grows larger
// than 1 minute.
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#

No puedes personalizar la estrategia de reintento predeterminada que usa la biblioteca de cliente de C#.

Go

Cuando inicializas un cliente de almacenamiento, se establece una configuración de reintentos predeterminada. A menos que se anulen, las opciones de la configuración se definen con los valores predeterminados. Los usuarios pueden configurar un comportamiento de reintento no predeterminado para una sola llamada de biblioteca (con BucketHandle.Retryer y ObjectHandle.Retryer) o para todas las llamadas realizadas por un cliente (con Client.SetRetry). Para modificar el comportamiento de reintento, pasa las RetryOptions pertinentes a uno de estos métodos.

Consulta el siguiente código de ejemplo para saber cómo personalizar el comportamiento de los reintentos.

import (
	"context"
	"fmt"
	"io"
	"time"

	"cloud.google.com/go/storage"
	"github.com/googleapis/gax-go/v2"
)

// configureRetries configures a custom retry strategy for a single API call.
func configureRetries(w io.Writer, bucket, object string) error {
	// bucket := "bucket-name"
	// object := "object-name"
	ctx := context.Background()
	client, err := storage.NewClient(ctx)
	if err != nil {
		return fmt.Errorf("storage.NewClient: %w", err)
	}
	defer client.Close()

	// Configure retries for all operations using this ObjectHandle. Retries may
	// also be configured on the BucketHandle or Client types.
	o := client.Bucket(bucket).Object(object).Retryer(
		// Use WithBackoff to control the timing of the exponential backoff.
		storage.WithBackoff(gax.Backoff{
			// Set the initial retry delay to a maximum of 2 seconds. The length of
			// pauses between retries is subject to random jitter.
			Initial: 2 * time.Second,
			// Set the maximum retry delay to 60 seconds.
			Max: 60 * time.Second,
			// Set the backoff multiplier to 3.0.
			Multiplier: 3,
		}),
		// Use WithPolicy to customize retry so that all requests are retried even
		// if they are non-idempotent.
		storage.WithPolicy(storage.RetryAlways),
	)

	// Use context timeouts to set an overall deadline on the call, including all
	// potential retries.
	ctx, cancel := context.WithTimeout(ctx, 500*time.Second)
	defer cancel()

	// Delete an object using the specified retry policy.
	if err := o.Delete(ctx); err != nil {
		return fmt.Errorf("Object(%q).Delete: %w", object, err)
	}
	fmt.Fprintf(w, "Blob %v deleted with a customized retry strategy.\n", object)
	return nil
}

Java

Cuando inicializas Storage, también se inicializa una instancia de RetrySettings. Si no se anulan, las opciones de RetrySettings se definen con los valores predeterminados. Para modificar el comportamiento predeterminado de reintento automático, pasa el StorageRetryStrategy personalizado al StorageOptions usado para crear la instancia Storage. Para modificar cualquiera de los otros parámetros escalares, pasa un RetrySettings personalizado al StorageOptions usado para crear la instancia Storage.

Consulta el siguiente ejemplo para saber cómo personalizar el comportamiento de reintento:


import com.google.api.gax.retrying.RetrySettings;
import com.google.cloud.storage.BlobId;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.StorageOptions;
import com.google.cloud.storage.StorageRetryStrategy;
import org.threeten.bp.Duration;

public final class ConfigureRetries {
  public static void main(String[] args) {
    String bucketName = "my-bucket";
    String blobName = "blob/to/delete";
    deleteBlob(bucketName, blobName);
  }

  static void deleteBlob(String bucketName, String blobName) {
    // Customize retry behavior
    RetrySettings retrySettings =
        StorageOptions.getDefaultRetrySettings().toBuilder()
            // Set the max number of attempts to 10 (initial attempt plus 9 retries)
            .setMaxAttempts(10)
            // Set the backoff multiplier to 3.0
            .setRetryDelayMultiplier(3.0)
            // Set the max duration of all attempts to 5 minutes
            .setTotalTimeout(Duration.ofMinutes(5))
            .build();

    StorageOptions alwaysRetryStorageOptions =
        StorageOptions.newBuilder()
            // Customize retry so all requests are retried even if they are non-idempotent.
            .setStorageRetryStrategy(StorageRetryStrategy.getUniformStorageRetryStrategy())
            // provide the previously configured retrySettings
            .setRetrySettings(retrySettings)
            .build();

    // Instantiate a client
    Storage storage = alwaysRetryStorageOptions.getService();

    // Delete the blob
    BlobId blobId = BlobId.of(bucketName, blobName);
    boolean success = storage.delete(blobId);

    System.out.printf(
        "Deletion of Blob %s completed %s.%n", blobId, success ? "successfully" : "unsuccessfully");
  }
}

Node.js

Cuando inicializas Cloud Storage, también se inicializa un archivo de configuración retryOptions. A menos que se anulen, las opciones de la configuración se definen con los valores predeterminados. Para modificar el comportamiento predeterminado de los reintentos, pasa la configuración de reintento personalizada retryOptions al constructor de almacenamiento al inicializarlo. La biblioteca de cliente de Node.js puede usar automáticamente estrategias de retardo exponencial para volver a intentar enviar solicitudes con el parámetro autoRetry.

Consulta el siguiente código de ejemplo para saber cómo personalizar el comportamiento de los reintentos.

/**
 * 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

Cuando inicializas un cliente de almacenamiento, se establece una configuración de reintentos predeterminada. A menos que se anulen, las opciones de la configuración se definen con los valores predeterminados. Los usuarios pueden configurar un comportamiento de reintento no predeterminado para un cliente o una sola llamada de operación pasando opciones de anulación en una matriz.

Consulta el siguiente código de ejemplo para saber cómo personalizar el comportamiento de los reintentos.

use Google\Cloud\Storage\StorageClient;

/**
 * Configures retries with customizations.
 *
 * @param string $bucketName The name of your Cloud Storage bucket.
 *        (e.g. 'my-bucket')
 */
function configure_retries(string $bucketName): void
{
    $storage = new StorageClient([
        // The maximum number of automatic retries attempted before returning
        // the error.
        // Default: 3
        'retries' => 10,

        // Exponential backoff settings
        // Retry strategy to signify that we never want to retry an operation
        // even if the error is retryable.
        // Default: StorageClient::RETRY_IDEMPOTENT
        'retryStrategy' => StorageClient::RETRY_ALWAYS,

        // Executes a delay
        // Defaults to utilizing `usleep`.
        // Function signature should match: `function (int $delay) : void`.
        // This function is mostly used internally, so the tests don't wait
        // the time of the delay to run.
        'restDelayFunction' => function ($delay) {
            usleep($delay);
        },

        // Sets the conditions for determining how long to wait between attempts to retry.
        // Function signature should match: `function (int $attempt) : int`.
        // Allows to change the initial retry delay, retry delay multiplier and maximum retry delay.
        'restCalcDelayFunction' => fn ($attempt) => ($attempt + 1) * 100,

        // Sets the conditions for whether or not a request should attempt to retry.
        // Function signature should match: `function (\Exception $ex) : bool`.
        'restRetryFunction' => function (\Exception $e) {
            // Custom logic: ex. only retry if the error code is 404.
            return $e->getCode() === 404;
        },

        // Runs after the restRetryFunction. This might be used to simply consume the
        // exception and $arguments b/w retries. This returns the new $arguments thus allowing
        // modification on demand for $arguments. For ex: changing the headers in b/w retries.
        'restRetryListener' => function (\Exception $e, $retryAttempt, &$arguments) {
            // logic
        },
    ]);
    $bucket = $storage->bucket($bucketName);
    $operationRetriesOverrides = [
        // The maximum number of automatic retries attempted before returning
        // the error.
        // Default: 3
        'retries' => 10,

        // Exponential backoff settings
        // Retry strategy to signify that we never want to retry an operation
        // even if the error is retryable.
        // Default: StorageClient::RETRY_IDEMPOTENT
        'retryStrategy' => StorageClient::RETRY_ALWAYS,

        // Executes a delay
        // Defaults to utilizing `usleep`.
        // Function signature should match: `function (int $delay) : void`.
        // This function is mostly used internally, so the tests don't wait
        // the time of the delay to run.
        'restDelayFunction' => function ($delay) {
            usleep($delay);
        },

        // Sets the conditions for determining how long to wait between attempts to retry.
        // Function signature should match: `function (int $attempt) : int`.
        // Allows to change the initial retry delay, retry delay multiplier and maximum retry delay.
        'restCalcDelayFunction' => fn ($attempt) => ($attempt + 1) * 100,

        // Sets the conditions for whether or not a request should attempt to retry.
        // Function signature should match: `function (\Exception $ex) : bool`.
        'restRetryFunction' => function (\Exception $e) {
            // Custom logic: ex. only retry if the error code is 404.
            return $e->getCode() === 404;
        },

        // Runs after the restRetryFunction. This might be used to simply consume the
        // exception and $arguments b/w retries. This returns the new $arguments thus allowing
        // modification on demand for $arguments. For ex: changing the headers in b/w retries.
        'restRetryListener' => function (\Exception $e, $retryAttempt, &$arguments) {
            // logic
        },
    ];
    foreach ($bucket->objects($operationRetriesOverrides) as $object) {
        printf('Object: %s' . PHP_EOL, $object->name());
    }
}

Python

Para modificar el comportamiento predeterminado de los reintentos, crea una copia del objeto google.cloud.storage.retry.DEFAULT_RETRY llamándolo con un método with_BEHAVIOR. La biblioteca de cliente de Python usa automáticamente estrategias de retardo para reintentar las solicitudes si incluyes el parámetro DEFAULT_RETRY.

Ten en cuenta que with_predicate no se admite en operaciones que obtienen o envían datos de carga útil a objetos, como las subidas y las descargas. Te recomendamos que modifiques los atributos uno a uno. Para obtener más información, consulta la referencia de reintentos de google-api-core.

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

Consulta el siguiente código de ejemplo para saber cómo personalizar el comportamiento de los reintentos.

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 timeout of 500 seconds (default=120 seconds).
    modified_retry = DEFAULT_RETRY.with_timeout(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 by default.
    # Pass in modified_retry to override the default retry behavior.
    print(
        f"The following library method is customized to be retried according to the following configurations: {modified_retry}"
    )

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

Ruby

Cuando inicializas el cliente de almacenamiento, todas las configuraciones de reintentos se definen con los valores que se muestran en la tabla anterior. Para modificar el comportamiento predeterminado de reintento, pasa las configuraciones de reintento al inicializar el cliente de almacenamiento.

Para anular el número de reintentos de una operación concreta, envía retries en el parámetro options de la operación.

def configure_retries bucket_name: nil, file_name: nil
  # The ID of your GCS bucket
  # bucket_name = "your-unique-bucket-name"

  # The ID of your GCS object
  # file_name = "your-file-name"

  require "google/cloud/storage"

  # Creates a client
  storage = Google::Cloud::Storage.new(

    # The maximum number of automatic retries attempted before returning
    # the error.
    #
    # Customize retry configuration with the maximum retry attempt of 5.
    retries: 5,

    # The total time in seconds that requests are allowed to keep being retried.
    # After max_elapsed_time, an error will be returned regardless of any
    # retry attempts made during this time period.
    #
    # Customize retry configuration with maximum elapsed time of 500 seconds.
    max_elapsed_time: 500,

    # The initial interval between the completion of failed requests, and the
    # initiation of the subsequent retrying request.
    #
    # Customize retry configuration with an initial interval of 1.5 seconds.
    base_interval: 1.5,

    # The maximum interval between requests. When this value is reached,
    # multiplier will no longer be used to increase the interval.
    #
    # Customize retry configuration with maximum interval of 45.0 seconds.
    max_interval: 45,

    # The multiplier by which to increase the interval between the completion
    # of failed requests, and the initiation of the subsequent retrying request.
    #
    # Customize retry configuration with an interval multiplier per iteration of 1.2.
    multiplier: 1.2
  )

  # Uses the retry configuration set during the client initialization above with 5 retries
  file = storage.service.get_file bucket_name, file_name

  # Maximum retry attempt can be overridden for each operation using options parameter.
  storage.service.delete_file bucket_name, file_name, options: { retries: 4 }
  puts "File #{file.name} deleted with a customized retry strategy."
end

APIs REST

Usa el algoritmo de tiempo de espera exponencial para implementar tu propia estrategia de reintentos.

Algoritmo de tiempo de espera exponencial

Un algoritmo de tiempo de espera exponencial vuelve a intentar enviar las solicitudes con tiempos de espera que aumentan exponencialmente entre las solicitudes, hasta un tiempo de espera máximo. Por lo general, debes usar un tiempo de espera exponencial con jitter para volver a enviar las solicitudes que cumplan los criterios de respuesta e idempotencia. Para consultar las prácticas recomendadas para implementar reintentos automáticos con tiempo de espera exponencial, consulte Addressing Cascading Failures (Cómo abordar los fallos en cascada).

Antipatrones de reintento

Te recomendamos que uses o personalices los mecanismos de reintento integrados cuando sea posible. Consulta Personalizar reintentos. Tanto si usas los mecanismos de reintento predeterminados como si los personalizas o implementas tu propia lógica de reintento, es fundamental evitar los siguientes antipatrones comunes, ya que pueden agravar los problemas en lugar de resolverlos.

Reintentando sin tiempo de espera

Si se reintenta enviar solicitudes inmediatamente o con retrasos muy cortos, se pueden producir fallos en cascada, es decir, fallos que pueden desencadenar otros fallos.

Cómo evitarlo: implementa un tiempo de espera exponencial con fluctuación. Esta estrategia aumenta progresivamente el tiempo de espera entre reintentos y añade un elemento aleatorio para evitar que los reintentos sobrecarguen el servicio.

Volver a intentar operaciones no idempotentes de forma incondicional

Si se ejecutan repetidamente operaciones que no son idempotentes, se pueden producir efectos secundarios no deseados, como sobrescrituras o eliminaciones de datos no deseadas.

Cómo evitarlo: consulta las características de idempotencia de cada operación en la sección sobre la idempotencia de las operaciones. En el caso de las operaciones no idempotentes, asegúrate de que tu lógica de reintentos pueda gestionar los posibles duplicados o evita reintentarlas por completo. Tenga cuidado con los reintentos que puedan provocar condiciones de carrera.

Reintentar errores que no se pueden reintentar

Tratar todos los errores como si se pudieran volver a intentar puede ser problemático. Algunos errores, como los fallos de autorización o las solicitudes no válidas, son persistentes. Si se vuelve a intentar realizar la acción sin solucionar la causa subyacente, no se completará correctamente y es posible que las aplicaciones se queden en un bucle infinito.

Cómo evitarlo: clasifica los errores en transitorios (se pueden volver a intentar) y permanentes (no se pueden volver a intentar). Solo se deben reintentar los errores transitorios, como los códigos HTTP 408, 429 y 5xx, o los problemas de conexión específicos. En el caso de los errores permanentes, regístralos y gestiona la causa subyacente de forma adecuada.

Ignorar los límites de reintentos

Si se reintenta indefinidamente, se pueden agotar los recursos de la aplicación o se pueden enviar solicitudes continuamente a un servicio que no se recuperará sin intervención.

Cómo evitarlo: adapta los límites de reintentos a la naturaleza de tu carga de trabajo. En el caso de las cargas de trabajo sensibles a la latencia, te recomendamos que definas una duración máxima total de reintentos para asegurarte de que se produzca una respuesta o un fallo a tiempo. En el caso de las cargas de trabajo por lotes, que pueden tolerar periodos de reintento más largos para errores transitorios del lado del servidor, considera la posibilidad de definir un límite de reintentos total más alto.

Reintentos innecesarios

Si se añade una lógica de reintento personalizada a nivel de aplicación sobre los mecanismos de reintento ya existentes, se puede producir un número excesivo de reintentos. Por ejemplo, si tu aplicación vuelve a intentar una operación tres veces y la biblioteca de cliente subyacente también lo hace tres veces por cada intento de tu aplicación, podrías acabar con nueve intentos. Si se envían muchos reintentos para errores que no se pueden reintentar, se puede limitar el número de solicitudes, lo que a su vez limita el rendimiento de todas las cargas de trabajo. Un número elevado de reintentos también puede aumentar la latencia de las solicitudes sin mejorar la tasa de éxito.

Cómo evitarlo: te recomendamos que uses y configures los mecanismos de reintento integrados. Si debes implementar reintentos a nivel de aplicación, como en el caso de una lógica empresarial específica que abarca varias operaciones, hazlo conociendo bien el comportamiento de reintento subyacente. Considera inhabilitar o limitar significativamente los reintentos en una de las capas para evitar efectos multiplicativos.

Siguientes pasos