Números de generación y condiciones previas

Los números de generación de objetos permiten a los usuarios identificar de forma única los recursos de datos y aplicar condiciones previas para garantizar la atomicidad de sus transacciones de varios pasos.

Generaciones

Incluso sin el Control de versiones de los objetos habilitado, todos los objetos de Cloud Storage tienen números de generación y de metageneración. El número de generación cambia cada vez que se reemplaza el objeto y el de metageneración cambia cada vez que se actualizan los metadatos del objeto.

Dado que los números de metageneración de objetos se restablecen en uno para cada generación de objetos nueva, son significativos solo cuando se sincronizan con un número de generación.

Los depósitos también conservan una cantidad de metageneraciones que permite a los usuarios identificar de forma única el estado de los metadatos de un depósito. Dado que los depósitos no tienen datos de carga útil y, por lo tanto, tampoco números de generación, sus números de metageneración son significativos por sí mismos.

Ejemplo: carga en paralelo

En las cargas en paralelo, divides un objeto en varios fragmentos, subes los fragmentos en una ubicación temporal de forma simultánea y usas compose en el objeto original a partir de estos fragmentos temporales. Si un proceso independiente usa de forma involuntaria el mismo nombre que uno o más fragmentos temporales que subiste, luego, cuando intentes compose el objeto, se usarán componentes incorrectos y el objeto se dañará.

Mediante el uso de los números de generación, se evita que suceda este daño. Si incluyes el número de generación de cada fragmento subido cuando realizas tu solicitud compose, compose ocurre con los fragmentos correctos o la solicitud falla con una respuesta 404 Not Found.

Condiciones previas

Las condiciones previas indican a Cloud Storage que solo realice una solicitud si el número de generación o metageneración del objeto afectado cumple con tus criterios de condición previa. Estas verificaciones de los números de generación y metageneración garantizan que el objeto se encuentre en el estado esperado, lo que te permite realizar actualizaciones seguras de lectura, modificación y escritura y operaciones condicionales en los objetos.

Cuando una condición previa match usa un número de generación o metageneración específico, el objeto de Cloud Storage al que la solicitud aplica debe tener el mismo número de generación y metageneración. Si lo hace, la solicitud se ejecuta de forma correcta. Si no lo hace, la solicitud falla y se muestra una respuesta 412 Precondition Failed.

Cuando una condición previa match usa el valor 0 en lugar de un número de generación, la solicitud solo se realiza de forma correcta si no hay objetos activos en el depósito de Cloud Storage con el nombre especificado en la solicitud. Si existe el objeto, la solicitud falla y muestra una respuesta 412 Precondition Failed.

Las condiciones previas, a menudo, se usan en las solicitudes cambiantes, cargas, borrado, copias o actualizaciones de metadatos, para evitar las condiciones de carrera. Las condiciones de carrera pueden surgir cuando la misma solicitud se envía varias veces o cuando los procesos independientes interfieren entre sí. Por ejemplo, los reintentos de solicitudes múltiples después de una interrupción en la red, o los usuarios que realizan una operación de lectura-modificación-escritura en el mismo objeto pueden crear condiciones de carrera.

Además de las condiciones previas que usan los números de generación y metageneración, también existen condiciones previas disponibles que usan ETag. Las ETag de la API de XML para objetos no compuestos solo cambian cuando cambia el contenido, mientras que las ETag de los objetos compuestos y los recursos de la API de JSON cambian siempre que el contenido o los metadatos cambien. Para obtener más información sobre las ETag, consulta ETag y Hashes: recomendaciones.

Costo de condiciones previas

Las condiciones previas tienen un costo de rendimiento y facturación: para cada operación cambiante, también envías una solicitud de metadatos GET facturable a fin de determinar el número de generación y metageneración del objeto. Como una consideración de rendimiento, las condiciones previas pueden duplicar de manera potencial 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. Como consideración sobre los precios, la solicitud de metadatos GET que te permite usar las condiciones previas se factura según una tarifa de $0.004 por 10,000 operaciones.

Según tu aplicación, existen maneras de evitar los costos de rendimiento y facturación asociados con el uso de condiciones previas, como las que se detallan a continuación:

  • 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.
  • Usar un esquema de nombres que evite más de un cambio del nombre del mismo objeto para que no tengas que usar las condiciones previas.
  • 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.
  • Recordar los resultados de las llamadas GET realizadas antes de los cambios.

Condiciones previas en la API de XML

En la API de XML, los números de generación y metageneración se exponen mediante los encabezados de las respuestas x-goog-generation y x-goog-metageneration. Estos encabezados se muestran en la respuesta de una solicitud HEAD para un objeto.

Consulta la Referencia de encabezados HTTP si deseas obtener una lista completa de encabezados de solicitudes de condiciones previas que puedes usar para hacer que la solicitud sea condicional al estado del objeto solicitado. Por ejemplo, puedes realizar estas acciones:

  • Usar la condición previa x-goog-if-generation-match para ejecutar una solicitud solo si el número de generación en la condición previa coincide con el número de generación del objeto solicitado. Si usas 0 en lugar de un número de generación, la solicitud solo se realiza de forma correcta si no existe un objeto activo en tu depósito que coincida con el objeto nombrado en la solicitud.

  • Usar las condiciones previas x-goog-if-metageneration-match para ejecutar una solicitud solo si el número de metageneración en el encabezado coincide con el número de metageneración del objeto solicitado.

  • Usar la condición previa If-Modified-Since con las solicitudes GET o HEAD. Estas solicitudes se ejecutan solo si el momento de creación para la generación del objetos más reciente, es decir, la última modificación del objeto, sucedió con anterioridad al tiempo especificado en la condición previa.

  • Usar las ETag y las condiciones previas If-Match o If-None-Match con las solicitudes GET o HEAD. Estas solicitudes se ejecutan solo si el objeto solicitado coincide o no con el ETag especificado en la condición previa.

  • Usa varias condiciones previas en la misma solicitud. Por ejemplo, si usas x-goog-if-generation-match, también puedes usar x-goog-if-metageneration-match.

Condiciones previas en la API de JSON

En la API de JSON, puedes obtener los números de generación y metageneración mediante las propiedades generation y metageneration de una respuesta que contiene un recurso de objeto o de depósito. Un recurso de objeto o de depósito se muestra en el cuerpo de la respuesta de una solicitud GET para el objeto o el depósito.

Para usar condiciones previas en las solicitudes, agrega las condiciones previas como parámetros de consulta al final de la URL. Para cada condición previa, agrega un parámetro de consulta con el mismo nombre que la condición previa. Por ejemplo, esta es una solicitud que usa ifGenerationMatch: https://www.googleapis.com/storage/v1/b/testgrid-triage-testing/o/test?ifGenerationMatch=1122334455

En este ejemplo, la API solo realiza esta solicitud si la generación del objeto es 1122334455.

La API de JSON también es compatible con las ETag HTTP 1.1 y los encabezados HTTP If-Match e If-None-Match correspondientes para todos los recursos, incluidos los depósitos, objetos y LCA. Se muestra una ETag como parte del encabezado de la respuesta cuando se muestra un recurso, como también se incluye en el mismo recurso.

Estos son algunos ejemplos de condiciones previas que puedes usar para realizar la solicitud condicional en el estado del objeto solicitado:

  • Usa las condiciones previas ifGenerationMatch y ifGenerationNotMatch para ejecutar una solicitud solo si el número de generación en la propiedad coincide o no con el número de generación del objeto solicitado. Si usas 0 en lugar de un número de generación en ifGenerationMatch, la solicitud solo se realiza de forma correcta si no hay un objeto activo en tu depósito que coincida con el objeto nombrado en la solicitud.

  • Usa las condiciones previas ifMetagenerationMatch y ifMetagenerationNotMatch para ejecutar una solicitud en un objeto o depósito. Las solicitudes se completan de forma correcta solo si el número de metageneración en la propiedad coincide o no con el número de metageneración del objeto o depósito solicitado.

  • Usa las ETag y las condiciones previas If-Match o If-None-Match como encabezados en las solicitudes. Estas solicitudes se ejecutan solo si el objeto solicitado coincide o no con el ETag especificado en la condición previa.

  • Usa varias condiciones previas en la misma solicitud. Por ejemplo, si usas ifGenerationMatch al tiempo que solicitas un objeto, también puedes usar ifMetagenerationMatch.

Ten en cuenta que no se aceptan las condiciones previas de generación y metageneración para las operaciones de la LCA; en su lugar, usa el recurso de entrada de control de acceso ETag. Puedes encontrarlo dentro de cada recurso de entrada de control de acceso, al que también se puede acceder desde el objeto que contiene o el recurso del depósito.

Ejemplos de las condiciones de carrera

Lectura, modificación y escritura en simultáneo

Un patrón común para actualizar los metadatos del objeto o depósito conlleva la lectura del estado actual, la aplicación de modificaciones de forma local y el envío de metadatos modificados a Cloud Storage para su escritura. Esto puede ser precario si dos o más procesos independientes intentan ejecutar la secuencia de forma simultánea.

Considera el ejemplo siguiente: deseas agregar una entrada de LCA para un colaborador a fin de poder acceder a tu depósito. Al mismo tiempo, un compañero de trabajo desea quitar un colaborador por separado que ya no necesita acceder al depósito.

Para ello, tú y tu compañero de trabajo leen el mismo estado inicial de los metadatos del depósito y cada uno realiza la modificación deseada en las entradas de LCA del depósito. Cuando escribes las modificaciones en Cloud Storage, se actualizan los metadatos de forma correcta. Por desgracia, tus cambios se perderán tan pronto como tu compañero de trabajo suba sus modificaciones, ya que no tenía forma de incluir tu actualización. Como resultado, se pierde la entrada de la LCA para tu colaborador. Ya no podrá acceder a tu depósito y nadie estará al tanto de lo que suceda (sin volver y observar las entradas de la LCA).

Prevención de la condición de carrera

Tú y tu compañero de trabajo pueden evitar esta condición de carrera si agregan una condición previa if-metageneration-match a cada una de las operaciones de escritura. En la condición previa, se debe usar el número de metageneración del depósito, que es parte de los metadatos que recibiste en la operación de lectura inicial.

Cuando tus modificaciones se agregan a la entrada de la LCA, el número de metageneración del depósito cambia. Ahora que se usan las condiciones previas, cuando tu compañero de trabajo escribe su versión de las entradas de la LCA, el número de metageneración del depósito no coincidirá con el número en la condición previa y se le informará de la actualización con errores con un código de respuesta 412 Precondition Failed. Tras recibir este código de respuesta, tu compañero de trabajo puede reaccionar en consecuencia, por ejemplo, realizando un ciclo nuevo de lectura, modificación y escritura con los metadatos actualizados.

Reintentos de solicitudes múltiples

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

Supón que deseas borrar el archivo file.txt almacenado en Cloud Storage. Después, quieres agregar un archivo nuevo con el mismo nombre a Cloud Storage.

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 eliminación para el objeto, que se realiza de forma correcta y recibes una respuesta de confirmación. Un minuto después, decides subir 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 existe un file.txt nuevo presente. Cloud Storage envía una respuesta que no recibes, ya que tu cliente dejó de escucharla. No solo el archivo nuevo se borra, al contrario de tu intención, sino que tampoco sabe 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 situación descrita con anterioridad, debes comenzar con la obtención de los metadatos de file.txt para determinar su generación actual. Luego, debes enviar la solicitud de borrado con una condición previa if-generation-match que use el número de generación. El uso de 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. Con la condición previa if-generation-match, cualquier intento involuntario por cambiar una generación diferente de file.txt falla con el código de respuesta 412 Precondition Failed.

Dado que las interrupciones de la red similares pueden ocasionar condiciones de carrera para la solicitud de carga que siguió a tu solicitud de borrado, puedes evitar muchas de estas con una if-generation-match:0 aplicada a 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 se realice de forma correcta, 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:

Limitaciones de if-generation-match:0

if-generation-match:0 no puede evitar que la creación del objeto se produzca dos veces si el primer objeto se borra, debido a que la ausencia del objeto no se identifica de forma única. Considera el caso siguiente, en el que ningún dato se pierde, pero terminas obteniendo un archivo que no esperabas:

  1. Comienzas con una solicitud GET para los metadatos de file.txt a fin de encontrar su número de generación. En la respuesta, notas que file.txt no existe.

  2. Ante esto, realizas una solicitud para subir file.txt con la condición previa if-generation-match:0, pero el tiempo de espera de la solicitud se agota cuando un enrutador intermedio pierde conectividad de forma temporal.

  3. Si fallas la primera vez, reintenta tu solicitud de carga, otra vez con la condición previa if-generation-match:0. Esta vez, la solicitud se realiza de forma correcta.

  4. Poco después, envía una solicitud para borrar file.txt, que se realiza de forma correcta.

  5. Si el enrutador que perdió conectividad ahora se conecta de nuevo y envía tu primera solicitud de carga a Cloud Storage, la condición previa que acompañó a la solicitud sigue siendo una coincidencia, por lo que se vuelve a crear file.txt. Con o sin la condición previa, file.txt se sube de manera inesperada una segunda vez.

¿Te sirvió esta página? Envíanos tu opinión:

Enviar comentarios sobre…

¿Necesitas ayuda? Visita nuestra página de asistencia.