O controle de simultaneidade otimista (OCC, na sigla em inglês) é uma estratégia para gerenciar recursos compartilhados e evitar atualizações perdidas ou disputas quando vários usuários ou processos modificam o mesmo recurso ao mesmo tempo.
Por exemplo, ao trabalhar com o Identity and Access Management, uma política do IAM é um recurso compartilhado anexado a um recurso, como um projeto, bucket ou serviço. Sem o controle de simultaneidade, modificações simultâneas podem substituir mudanças recentes na política, revogando inadvertidamente o acesso legítimo ou concedendo permissões não autorizadas.
Para implementar o OCC, os sistemas normalmente usam um número de versão ou um campo etag (tag de entidade) na estrutura do recurso.
Como funciona o OCC
Suponha que dois processos, A e B, tentem atualizar um recurso compartilhado ao mesmo tempo:
- O processo A lê o estado atual do recurso.
- O processo B lê o mesmo estado atual.
- O processo A modifica a cópia dele e a grava de volta no servidor.
- O processo B modifica a cópia dele e a grava de volta no servidor.
Como o Processo B substitui o recurso sem saber que o Processo A já o alterou, as atualizações do Processo A são perdidas.
O OCC resolve esse problema introduzindo uma impressão digital exclusiva que muda sempre que uma entidade é modificada. Em muitos sistemas, como o IAM, essa
impressão digital é um etag. O servidor verifica essa tag em cada gravação:
- Quando você lê o recurso, o servidor retorna um
etag(uma impressão digital exclusiva). - Ao enviar o recurso modificado de volta, inclua o
etagoriginal. - Se o
etagarmazenado não corresponder aoetagenviado, o que significa que outro processo modificou o recurso depois que você o leu, a operação de gravação vai falhar com um erroABORTEDouFAILED_PRECONDITION.
Essa falha sinaliza ao cliente para repetir todo o processo: ler novamente o estado atualizado, aplicar novamente as alterações e tentar a gravação novamente com o novo etag.
Implementar o loop de OCC
O núcleo de uma implementação de OCC é um loop de repetição que lê, modifica e tenta salvar o recurso, saindo após uma gravação bem-sucedida. Para evitar loops infinitos durante alta contenção, configure um número máximo razoável de novas tentativas.
Etapas do loop
A tabela a seguir descreve cada etapa em um loop de repetição de OCC:
| Etapa | Ação | Exemplo de implementação |
|---|---|---|
| Read | Busque o estado atual do recurso, incluindo o etag. |
let mut policy = client .get_iam_policy() .set_resource(&secret_name) .send() .await?; |
| Modificar | Aplique as mudanças à struct local. | policy.bindings.push(new_binding); |
| Escrever e verificar | Tente salvar o recurso modificado usando o etag atual e verifique se há códigos do erro de simultaneidade. |
match client .set_iam_policy() .set_resource(&secret_name) .set_policy(policy) .send() .await { Ok(p) => return Ok(p), Err(e) => { /* ... */ } } |
| Sucesso ou nova tentativa | Se a gravação for bem-sucedida, saia do loop. Se falhar com um erro de simultaneidade, incremente o contador de novas tentativas e continue o loop na etapa Read. | - |
Exemplo
O exemplo a seguir demonstra como implementar um loop de OCC que atualiza uma
política do IAM em um recurso de secret do Secret Manager
concedendo a um membro o papel roles/secretmanager.secretAccessor.
Para executar este exemplo, primeiro declare a dependência no arquivo Cargo.toml:
cargo add google-cloud-secretmanager-v1
Em seguida, implemente o loop de repetição do OCC:
Preencher código
O código a seguir mostra a função completa com todas as importações necessárias: