Solicita condiciones previas

En esta página, se analizan las condiciones previas de solicitud, que usas para evitar que las solicitudes se apliquen a un recurso cuando este se encuentra en un estado inesperado.

Introducción

Cuando se usan condiciones previas en una solicitud a Cloud Storage, la solicitud solo continúa si el recurso de destino cumple con los criterios definidos en las condiciones previas. Las verificaciones de condiciones previas garantizan que un objeto o bucket esté en el estado esperado, lo que te permite realizar actualizaciones seguras de lectura, modificación y escritura y operaciones condicionales.

Las condiciones previas suelen usarse para evitar las condiciones de carrera en solicitudes cambiantes, como cargas, eliminaciones o actualizaciones de metadatos. Las condiciones de carrera pueden surgir cuando la misma solicitud se envía varias veces o cuando distintos procesos independientes intentan modificar el mismo recurso. Consulta Ejemplos de condiciones de carrera y corrupción de datos para obtener más información. Las condiciones previas también se usan cuando se recuperan metadatos y datos de objetos en solicitudes sucesivas a fin de garantizar que el objeto no haya cambiado en el tiempo entre las dos solicitudes.

Criterios de condición previa

Cloud Storage admite el uso de varias propiedades diferentes de recursos inmutables en las condiciones previas:

En la siguiente tabla, se enumeran las condiciones previas que admiten la API de JSON y la API de XML:

API de JSON API de XML Descripción
Parámetro de consulta ifGenerationMatch Encabezado x-goog-if-generation-match La solicitud continúa si el generation del recurso de destino coincide con el valor usado en la condición previa. Si los valores no coinciden, la solicitud falla con una respuesta 412 Precondition Failed.
Parámetro de consulta ifMetagenerationMatch Encabezado x-goog-if-metageneration-match La solicitud continúa si el metageneration del recurso de destino coincide con el valor usado en la condición previa. Si los valores no coinciden, la solicitud falla con una respuesta 412 Precondition Failed.
Parámetro de consulta ifGenerationNotMatch N/A La solicitud continúa si el generation del recurso de destino no coincide con el valor usado en la condición previa. Si los valores coinciden, la solicitud falla con una respuesta 304 Not Modified.
Parámetro de consulta ifMetagenerationNotMatch N/A La solicitud continúa si el metageneration del recurso de destino no coincide con el valor usado en la condición previa. Si los valores coinciden, la solicitud falla con una respuesta 304 Not Modified.
Encabezado If-Match Encabezado If-Match Aplicable a las solicitudes que recuperan datos. La solicitud continúa si el ETag del recurso de destino coincide con el valor usado en la condición previa. Si los valores no coinciden, la solicitud falla con una respuesta 412 Precondition Failed.
Encabezado If-None-Match Encabezado If-None-Match Aplicable a las solicitudes que recuperan datos. La solicitud continúa si el ETag del recurso de destino no coincide con el valor usado en la condición previa. Si los valores coinciden, la solicitud falla con una respuesta 304 Not Modified.
N/A Encabezado If-Modified-Since La solicitud continúa si el recurso de destino tiene una fecha Last-Modified posterior al valor usado en la condición previa. Si el recurso de destino no cumple con esta condición previa, la solicitud falla con una respuesta 304 Not Modified.
N/A Encabezado If-Unmodified-Since La solicitud continúa si el recurso de destino tiene una fecha Last-Modified previa o igual al valor usado en la condición previa. Si el recurso de destino no cumple con esta condición previa, la solicitud falla con una respuesta 412 Precondition Failed.

Condiciones previas de la composición de objetos

Cuando se realiza una composición de objetos, la API de JSON y la API de XML admiten lo siguiente:

  • Las condiciones de previas de coincidencia de generación y metageneración para el objeto de destino.

  • La condición previa de coincidencia de generación para cada objeto de origen. El uso de esta condición previa evita que se usen componentes incorrectos en el caso de que un proceso independiente reemplace uno de los componentes previstos de la composición. Si usas condiciones previas y ese reemplazo se produce, las operaciones compose fallan con una respuesta 412 Precondition Failed.

Condiciones previas de copia de objetos

Cuando copias o reescribes un objeto en Cloud Storage, la API de JSON y la API de XML admiten el uso de condiciones previas estándar para el objeto de destino. Cada API tiene compatibilidad adicional con las condiciones previas para los objetos de origen:

  • La API de JSON admite las condiciones previas de generación y metageneración del objeto de origen, que se especifican mediante el uso de parámetros de consulta con el prefijo ifSource.

  • Todas las condiciones previas que admite la API de XML se pueden usar en el objeto de origen. Estas condiciones previas se especifican en los encabezados con el prefijo x-goog-copy-source-.

El valor 0 en una condición previa de coincidencia de generación

La condición previa de coincidencia de generación acepta el valor 0 como un caso especial. Cuando se incluye una condición previa de coincidencia de generación con un valor de 0 en una solicitud, la solicitud solo procede si no existe un objeto con el nombre especificado en el bucket o si solo hay versiones no actuales del objeto en el bucket. Si hay una versión publicada con el nombre especificado, la solicitud falla con un código de estado de 412 Precondition Failed.

Prácticas recomendadas y consideraciones

  • Puedes usar varias condiciones previas en una sola solicitud. Si no se cumple alguna de las condiciones previas, fallará la solicitud general.

  • Los depósitos no tienen un número de generación, aunque sí tienen un número de metageneración. No debes usar condiciones previas que especifiquen un número de generación en una solicitud de bucket.

  • Si usas una condición previa de metageneración en una solicitud de objeto, siempre debes usar una condición previa de generación también. Esto evita que la solicitud sea correcta en un objeto diferente que, por coincidencia, tiene un número de metageneración que pasa la condición previa.

  • Para los depósitos que tienen versiones de objetos tanto activos como no actuales, las solicitudes de objetos no se aplican a las versiones no actuales, a menos que un número de generación se incluya de forma explícita en la solicitud. Esto significa que, para una solicitud general que usa condiciones previas, la solicitud falla si la versión publicada no coincide con la condición previa, sin importar si una versión no actual coincide con las condiciones previas.

  • Por lo general, debes usar las condiciones previas de generación y metageneración en lugar de las condiciones previas de ETag. Los números de generation y metageneration hacen juntos un seguimiento de todas las actualizaciones de objetos, incluidos los cambios de metadatos, lo que proporciona una garantía más sólida que las ETags. Además, los números de generación y metageneración son coherentes en todas las API, mientras que las ETags no lo son.

  • Las condiciones previas no se pueden usar en cargas multiparte de la API de XML. Intentar hacerlo da como resultado un error 400 NotImplemented.

Costo de condiciones previas

Muchas arquitecturas que usan condiciones previas requieren que realices una solicitud de metadatos de objeto antes de la solicitud principal, para determinar el número de generación o metageneración actual.

  • Una solicitud adicional significa que puedes duplicar la parte de la red de la latencia general de la operación si agregas un recorrido de ida y vuelta adicional, que puede ser un factor importante en las operaciones sensibles a la latencia.

Según tu aplicación, existen formas de reducir los impactos del uso de las condiciones previas, como las siguientes:

  • Almacenar los números de generación y metageneración de tus objetos de forma local a fin de que conozcas los números correctos para usar en tu condición previa
  • Tener conocimiento de la aplicación sobre qué objetos se crearon recientemente, a fin de que sepas cuándo usar la condición previa if-generation-match:0

Ejemplo: Usa una condición previa

En el siguiente ejemplo, se usa la condición previa de coincidencia de generación en una solicitud de carga de un objeto. Para que la solicitud continúe, debe haber un objeto preexistente almacenado en el bucket con el nombre especificado y el número de generación del objeto preexistente debe coincidir con el número proporcionado en la condición previa:

Línea de comandos

Usa la marca --if-generation-match junto con el comando normal:

gcloud storage cp OBJECT_LOCATION gs://DESTINATION_BUCKET_NAME --if-generation-match=GENERATION

Aquí:

  • GENERATION es el número de generación previsto del objeto que deseas reemplazar. Por ejemplo, 1122334455667788

  • OBJECT_LOCATION es la ruta local hacia tu objeto. Por ejemplo, Desktop/dog.png.

  • DESTINATION_BUCKET_NAME es el nombre del bucket al que subes el objeto. Por ejemplo, my-bucket

API de JSON

  1. Obtén un token de autorización de acceso de OAuth 2.0 Playground. Configura Playground para usar tus credenciales de OAuth. Para obtener instrucciones, consulta Autenticación de la API.
  2. Usa cURL para llamar a la API de JSON con una solicitud de objeto POST:

    curl -X POST --data-binary @OBJECT_LOCATION \
      -H "Authorization: Bearer OAUTH2_TOKEN" \
      -H "Content-Type: OBJECT_CONTENT_TYPE" \
      "https://storage.googleapis.com/upload/storage/v1/b/BUCKET_NAME/o?uploadType=media&name=OBJECT_NAME"&ifGenerationMatch=GENERATION"

    Aquí:

    • OBJECT_LOCATION es la ruta de acceso local a tu objeto. Por ejemplo, Desktop/dog.png.
    • OAUTH2_TOKEN es el token de acceso que generaste en el paso 1.
    • OBJECT_CONTENT_TYPE es el tipo de contenido del objeto. Por ejemplo, image/png
    • BUCKET_NAME es el nombre del bucket al que subes el objeto. Por ejemplo, my-bucket
    • OBJECT_NAME es el nombre que deseas dar a tu objeto. Por ejemplo, dog.png
    • GENERATION es el número de generación previsto del objeto que deseas reemplazar. Por ejemplo, 1122334455667788

API de XML

  1. Obtén un token de autorización de acceso de OAuth 2.0 Playground. Configura Playground para usar tus credenciales de OAuth. Para obtener instrucciones, consulta Autenticación de la API.
  2. Usa cURL para llamar a la API de XML con una solicitud de objeto PUT:

    curl -X PUT --data-binary @OBJECT_LOCATION \
      -H "Authorization: Bearer OAUTH2_TOKEN" \
      -H "Content-Type: OBJECT_CONTENT_TYPE" \
      -H "x-goog-if-generation-match: GENERATION" \
      "https://storage.googleapis.com/BUCKET_NAME/OBJECT_NAME"

    Aquí:

    • OBJECT_LOCATION es la ruta de acceso local a tu objeto. Por ejemplo, Desktop/dog.png.
    • OAUTH2_TOKEN es el token de acceso que generaste en el paso 1.
    • OBJECT_CONTENT_TYPE es el tipo de contenido del objeto. Por ejemplo, image/png
    • GENERATION es el número de generación previsto del objeto que deseas reemplazar. Por ejemplo, 1122334455667788
    • BUCKET_NAME es el nombre del bucket al que subes el objeto. Por ejemplo, my-bucket
    • OBJECT_NAME es el nombre que deseas dar a tu objeto. Por ejemplo, dog.png

Casos en los que se usan condiciones previas

En las siguientes situaciones, se exploran las condiciones de carrera y los ejemplos de almacenamiento en caché que se benefician del uso de las condiciones previas.

Reintentos de varias solicitudes

Cloud Storage es un sistema distribuido. Debido a que las solicitudes pueden fallar por las condiciones de la red o del servicio, la forma recomendada de reintentar los errores es con una retirada exponencial. Sin embargo, debido a la naturaleza de los sistemas distribuidos, en ocasiones, estos reintentos pueden generar un comportamiento inesperado.

Considera el ejemplo siguiente: deseas borrar un objeto, file.txt, almacenado en uno de tus buckets. Después, deseas agregar un objeto nuevo con el mismo nombre al bucket. A fin de lograrlo, debes enviar una solicitud de borrado para el objeto. Sin embargo, una condición de red, como un enrutador intermedio que pierde conectividad de manera temporal, evita que la solicitud llegue a Cloud Storage, y tú no recibes respuesta alguna.

Debido a que no recibiste una respuesta a la primera solicitud, envías una segunda solicitud de borrado para el objeto, que se realiza de forma correcta y recibes una respuesta de confirmación. Un minuto después, subes un file.txt nuevo, y la carga se realiza de forma correcta.

Si el enrutador que perdió conectividad vuelve a conectarse y envía la solicitud de borrado original a Cloud Storage que aparentemente se había perdido, surge una condición de carrera. Cuando la solicitud llega a Cloud Storage, se ejecuta de forma correcta porque hay un file.txt nuevo. Cloud Storage envía una respuesta que no recibes porque tu cliente dejó de escucharla. El archivo nuevo se borra, al contrario de tu intención, y no sabes que se produjo la segunda operación de eliminación.

En el diagrama siguiente, se muestra lo que sucedió:

Prevención de la condición de carrera

A fin de evitar que suceda la anterior situación descrita, debes comenzar con la obtención de los metadatos de file.txt para determinar su generación actual. Luego, usas la generación en una condición previa de coincidencia de generación que incluyes como parte de la solicitud de borrado. La condición previa garantiza que solo el objeto con el número de generación específico se borre, sin importar cuándo llega la solicitud de borrado a Cloud Storage o cuántas veces se envía con la condición previa. Cualquier intento involuntario por borrar una generación diferente de file.txt falla con el código de respuesta 412 Precondition Failed.

Dado que las interrupciones de red similares pueden ocasionar condiciones de carrera para la solicitud de carga que siguió a tu solicitud de borrado, puedes evitar muchas de estas condiciones de carrera si usas el valor 0 en una condición previa de coincidencia de generación incluida en la solicitud de carga. El uso de esta condición previa garantiza que los reintentos de la carga no escriban dos veces el objeto de forma accidental, porque la condición previa permite que la solicitud proceda solo si no existen generaciones actuales del objeto.

Con estas condiciones previas implementadas, proteges tus datos a fin de evitar que se pierdan de forma accidental cuando se realizan las solicitudes de borrado y carga. Esto puede verse en el diagrama siguiente:

Asociación de metadatos del objeto

Los datos y metadatos de un objeto son entidades separadas que definen el objeto en Cloud Storage. Debido a que existen por separado, es posible que los datos del objeto cambien mientras trabajas con los metadatos del objeto.

Considera los siguientes casos:

  • Deseas descargar los metadatos y los datos de un objeto, que deben recuperarse de Cloud Storage en dos solicitudes separadas. Primero, solicitas los metadatos del objeto, pero antes de que puedas solicitar los datos del objeto, un proceso independiente o un usuario reemplaza el objeto. Tu solicitud de datos del objeto sigue siendo correcta, pero ahora tienes los metadatos del objeto anterior y los datos del objeto nuevo.

  • Deseas actualizar los metadatos de un objeto, por lo que debes recuperar los metadatos actuales del objeto para determinar su estado actual. Antes de que puedas enviar la solicitud para actualizar los metadatos con las modificaciones que deseas, un proceso independiente o un usuario reemplaza el objeto. Tu solicitud para cambiar los metadatos del objeto nuevo sigue siendo exitosa, pero ahora está asociada con datos de objetos diferentes de los que deseas.

Prevención de la condición de carrera

Para evitar que ocurran estas situaciones, debes usar el número de generación que se muestra en la solicitud inicial de metadatos de objeto y, luego, usar este valor en una condición previa de coincidencia de generación en la segunda solicitud. Esto garantiza que los metadatos coincidan de forma correcta con los datos o que la segunda solicitud falle con un código de respuesta 412 Precondition Failed, lo que te permite solicitar los metadatos correctos para el objeto nuevo.

Si te preocupa que los metadatos del objeto puedan cambiar entre la primera y la segunda solicitud, también puedes copiar el número de metageneración que se encuentra en la solicitud inicial y usarlo en una condición previa de coincidencia de metageneración en la segunda solicitud.

Actualidad de la copia local

En los casos en los que tengas una copia local de un objeto almacenado en Cloud Storage, a menudo es recomendable mantener tu copia local actualizada con la copia almacenada en tu bucket. Sin embargo, si el objeto almacenado en tu bucket no cambia, no querrás desperdiciar tiempo ni recursos volviendo a descargarlo, en especial si el objeto es grande.

Para evitar descargas innecesarias de contenido que todavía esté actualizado, puedes usar el número de generación de tu copia local como valor en una condición previa de no coincidencia de generación que incluyas en tu solicitud de descarga:

  • Si los datos de tu bucket siguen coincidiendo con la copia local, los números de generación coincidirán, lo que hará que la condición previa falle. Como resultado, la solicitud general falla con una respuesta 304 Not Modified y los datos no se descargan de forma innecesaria.

  • Si los datos en tu bucket cambiaron, los números de generación no coinciden y la condición previa es correcta. Esto significa que la solicitud general procede con normalidad y descarga la versión actualizada del contenido.

¿Qué sigue?