El control de simultaneidad optimista (OCC) es una estrategia para administrar recursos compartidos y evitar la pérdida de actualizaciones o condiciones de carrera cuando varios usuarios o procesos modifican el mismo recurso al mismo tiempo.
Por ejemplo, cuando trabajas con Identity and Access Management, una política de IAM es un recurso compartido adjunto a un recurso, como un proyecto, un bucket o un servicio. Sin el control de simultaneidad, las modificaciones simultáneas pueden reemplazar los cambios recientes en las políticas, lo que revoca de forma involuntaria el acceso legítimo o otorga permisos no autorizados.
Para implementar el OCC, los sistemas suelen usar un número de versión o un campo etag (etiqueta de entidad) en la estructura del recurso.
Cómo funciona el OCC
Supongamos que dos procesos, A y B, intentan actualizar un recurso compartido al mismo tiempo:
- El proceso A lee el estado actual del recurso.
- El proceso B lee el mismo estado actual.
- El proceso A modifica su copia y la vuelve a escribir en el servidor.
- El proceso B modifica su copia y la vuelve a escribir en el servidor.
Debido a que el proceso B reemplaza el recurso sin saber que el proceso A ya lo cambió, se pierden las actualizaciones del proceso A.
El OCC soluciona este problema con la introducción de una huella digital única que cambia cada
vez que se modifica una entidad. En muchos sistemas, como IAM, esta
huella digital es un etag. El servidor verifica esta etiqueta en cada escritura:
- Cuando lees el recurso, el servidor devuelve una
etag(una huella digital única). - Cuando vuelvas a enviar el recurso modificado, debes incluir el
etagoriginal. - Si el
etagalmacenado no coincide con eletagque enviaste, lo que significa que otro proceso modificó el recurso después de que lo leíste, la operación de escritura falla con un errorABORTEDoFAILED_PRECONDITION.
Esta falla indica al cliente que vuelva a intentar todo el proceso: volver a leer el
estado actualizado, volver a aplicar los cambios y volver a intentar la escritura con el nuevo
etag.
Implementa el bucle de OCC
El núcleo de una implementación de OCC es un bucle de reintentos que lee, modifica y guarda el recurso, y sale después de una escritura exitosa. Para evitar bucles infinitos durante una contención alta, configura una cantidad máxima razonable de intentos de reintento.
Pasos del bucle
En la siguiente tabla, se describe cada paso de un bucle de reintentos de OCC:
| Paso | Acción | Ejemplo de implementación |
|---|---|---|
| Lectura | Recupera el estado actual del recurso, incluido el etag. |
let mut policy = client .get_iam_policy() .set_resource(&secret_name) .send() .await?; |
| Modificar | Aplica los cambios a la estructura local. | policy.bindings.push(new_binding); |
| Escribe y verifica | Intenta guardar el recurso modificado con el etag existente y verifica si hay códigos de error de simultaneidad. |
match client .set_iam_policy() .set_resource(&secret_name) .set_policy(policy) .send() .await { Ok(p) => return Ok(p), Err(e) => { /* ... */ } } |
| Éxito o reintento | Si la escritura se realiza correctamente, sal del bucle. Si falla con un error de simultaneidad, incrementa el contador de reintentos y continúa el bucle desde el paso Lectura. | - |
Ejemplo
En el siguiente ejemplo, se muestra cómo implementar un bucle de OCC que actualiza una
política de IAM en un recurso de Secret Manager otorgando
a un miembro el rol roles/secretmanager.secretAccessor.
Para ejecutar este ejemplo, primero declara la dependencia en tu archivo Cargo.toml:
cargo add google-cloud-secretmanager-v1
Luego, implementa el bucle de reintentos de OCC:
Completar código
En el siguiente código, se muestra la función completa con todas las importaciones requeridas: