Números de geração e condições prévias

Com os números de geração de objeto, os usuários podem identificar recursos de dados específicos e aplicar condições prévias para garantir a atomicidade das transações em várias etapas.

Gerações

Mesmo quando o Controle de versão de objetos não está ativado, todos os objetos do Cloud Storage têm números de geração e de metageração. O número de geração é alterado sempre que o objeto é sobrescrito, e o número da metageração é alterado sempre que os metadados do objeto são atualizados.

Como os números de metageração de objeto são redefinidos para 1 para cada nova geração de objeto, eles são significativos apenas quando pareados com um número de geração.

Os intervalos também mantêm um número de metageração, que permite que os usuários identifiquem com exclusividade um estado de metadados do intervalo. Como os intervalos não têm dados de payload e, portanto, nenhum número de geração, os números de metageração são significativos por si só.

Exemplo: upload em paralelo

Nos uploads em paralelo, você divide um objeto em várias partes, faz upload delas em um local temporário simultaneamente e realiza uma operação compose para recompor o objeto original a partir das partes temporárias. Se um processo independente inadvertidamente estiver usando o mesmo nome de uma ou mais partes temporárias enviadas por upload, quando você tentar recompor o objeto com a operação compose, os componentes incorretos serão usados e o objeto ficará corrompido.

Ao usar números de geração, você evita que isso aconteça. Se você incluir o número de geração de cada parte enviada no upload quando fizer sua solicitação compose, a operação compose ocorrerá com as partes corretas ou a solicitação falhará com uma resposta 404 Not Found.

Condições prévias

As condições prévias dizem ao Cloud Storage para executar uma solicitação somente se o número de geração ou metageração do objeto afetado atender aos critérios de condição prévia. Essas verificações dos números de geração e metageração garantem que o objeto esteja no estado esperado, permitindo que você execute atualizações seguras de leitura/modificação/gravação e operações condicionais em objetos.

Quando uma condição prévia match usa um número de geração ou metageração específico, o objeto do Cloud Storage alvo da solicitação precisa ter o mesmo número de geração ou metageração. Caso isso ocorra, a solicitação será bem-sucedida. Mas se não for o caso, a solicitação falhará e uma resposta 412 Precondition Failed será retornada.

Quando uma condição prévia match usa o valor 0, em vez de um número de geração, a solicitação será bem-sucedida apenas se não houver objetos ativos no intervalo do Cloud Storage com o nome especificado na solicitação. Caso haja, a solicitação falhará e uma resposta 412 Precondition Failed será retornada.

As condições prévias são frequentemente usadas em solicitações de mutação (uploads, exclusões, cópias ou atualizações de metadados) para evitar disputas. Disputas podem surgir quando a mesma solicitação é enviada repetidamente ou quando processos independentes interferem uns com os outros. Por exemplo, várias tentativas de solicitação após a interrupção da rede ou usuários realizando uma operação de leitura/modificação/gravação no mesmo objeto podem criar disputas.

Além das condições prévias que usam números de geração e metageração, também há condições prévias disponíveis que usam ETags. As ETags da API XML para objetos não compostos mudam apenas quando o conteúdo é alterado, enquanto as ETags para objetos compostos e recursos da API JSON mudam sempre que o conteúdo ou os metadados são alterados. Para mais informações sobre ETags, consulte Hashes e ETags: práticas recomendadas.

Custo das condições prévias

As condições prévias têm um custo de desempenho e faturamento: para cada operação de mutação, você também emite uma solicitação de metadados GET faturável para determinar o número de geração/metageração do objeto. Em termos de desempenho, as condições prévias têm o potencial de dobrar a parte da rede da latência geral da operação porque adiciona uma viagem de ida e volta extra, o que pode ser um fator importante em operações afetadas pela latência. Em termos de preço, a solicitação de metadados GET que permite usar as condições prévias é faturada a uma taxa de US$ 0,004 por 10.000 operações.

Dependendo do seu aplicativo, há maneiras de evitar custos de desempenho e faturamento associados ao uso das condições prévias, como as medidas a seguir:

  • Armazenar os números de geração e metageração dos objetos localmente para que você saiba quais são os números corretos a serem usados na condição prévia.
  • Usar um esquema de nomenclatura que evita mais de uma operação de mutação do mesmo nome de objeto, para que não seja necessário utilizar condições prévias.
  • Ter conhecimento de quais objetos foram criados recentemente no aplicativo para que você saiba quando usar a condição prévia if-generation-match:0.
  • Lembrar dos resultados de chamadas GET realizadas antes de operações de mutação.

Condições prévias na API XML

Na API XML, os números de geração e metageração são expostos pelos cabeçalhos de resposta x-goog-generation e x-goog-metageneration. Esses cabeçalhos são retornados na resposta de uma solicitação HEAD de objeto.

Consulte a referência de cabeçalhos HTTP para uma listagem completa dos cabeçalhos de solicitação de condição prévia que podem ser usados para tornar a solicitação condicional ao estado do objeto solicitado. Por exemplo, é possível fazer o seguinte:

  • Use a condição prévia x-goog-if-generation-match para executar uma solicitação apenas se o número de geração na condição prévia corresponder ao número de geração do objeto solicitado. Se, em vez de um número de geração, você usar 0, a solicitação será bem-sucedida somente se não houver qualquer objeto ativo no intervalo com o mesmo nome do objeto da solicitação.

  • Use a condição prévia x-goog-if-metageneration-match para executar uma solicitação apenas se o número de metageração no cabeçalho corresponder ao número de metageração do objeto solicitado.

  • Use a condição prévia If-Modified-Since com solicitações GET ou HEAD. Essas solicitações são executadas somente se o horário da criação mais recente do objeto, ou seja, a última modificação dele, for mais recente do que o horário especificado na condição prévia.

  • Use ETags e as condições prévias If-Match ou If-None-Match com solicitações GET ou HEAD. Essas solicitações são executadas somente se o objeto solicitado corresponder ou não à ETag especificada na condição prévia.

  • Use várias condições prévias na mesma solicitação. Por exemplo, se você estiver usando x-goog-if-generation-match, use também x-goog-if-metageneration-match.

Condições prévias na API JSON

Na API JSON, é possível conseguir os números de geração e metageração nas propriedades generation e metageneration de uma resposta que contém um recurso de objeto ou um recurso de intervalo. Um recurso de objeto ou intervalo é retornado no corpo da resposta de uma solicitação GET para o objeto ou para o intervalo.

Para usar condições prévias em solicitações, adicione-as como parâmetros de consulta no final do URL. Para cada condição prévia, adicione um parâmetro de consulta com o mesmo nome dela. Por exemplo, esta é uma solicitação que usa ifGenerationMatch: https://www.googleapis.com/storage/v1/b/testgrid-triage-testing/o/test?ifGenerationMatch=1122334455

Nesse exemplo, a API realizará essa solicitação somente se a geração do objeto for 1122334455.

A API JSON também aceita ETags HTTP 1.1 e os cabeçalhos HTTP If-Match e If-None-Match correspondentes para todos os recursos, incluindo intervalos, objetos e listas de controle de acesso (ACLs, na sigla em inglês). Uma ETag é retornada como parte do cabeçalho da resposta sempre que um recurso é retornado, bem como incluída no próprio recurso.

Veja abaixo alguns exemplos de condições prévias que podem ser usadas para tornar a solicitação condicional ao estado do objeto solicitado:

  • Use as condições prévias ifGenerationMatch e ifGenerationNotMatch para executar uma solicitação somente se o número de geração na propriedade corresponder ou não ao número de geração do objeto solicitado. Se, em vez de um número de geração, você usar 0 em ifGenerationMatch, a solicitação será bem-sucedida somente se não houver qualquer objeto ativo no intervalo com o mesmo nome do objeto da solicitação.

  • Use as condições prévias ifMetagenerationMatch e ifMetagenerationNotMatch para executar uma solicitação em um intervalo ou objeto. As solicitações serão bem-sucedidas somente se o número de metageração na propriedade corresponder ou não ao número de metageração do intervalo ou objeto solicitado.

  • Use ETags e as condições prévias If-Match ou If-None-Match como cabeçalhos nas solicitações. Essas solicitações são executadas somente se o objeto solicitado corresponder ou não à ETag especificada na condição prévia.

  • Use várias condições prévias na mesma solicitação. Por exemplo, se você estiver usando ifGenerationMatch ao solicitar um objeto, use também ifMetagenerationMatch.

Observe que condições prévias de geração e metageração não são aceitas para operações de ACL. Em vez disso, use a ETag do recurso de entrada de controle de acesso. Isso pode ser encontrado dentro de cada recurso de entrada de controle de acesso, que também pode ser acessado no objeto de contenção ou recurso do intervalo.

Exemplos de condições de corrida

Leitura-modificação-gravação simultânea

Um padrão comum para atualizar metadados de intervalo ou objeto envolve ler o estado atual, aplicar modificações localmente e enviar os metadados modificados de volta ao Cloud Storage para gravação. Isso pode ser precário se dois ou mais processos independentes tentarem a sequência simultaneamente.

Imagine este caso: você quer adicionar uma entrada de ACL a um colaborador para que ele possa acessar seu intervalo. Ao mesmo tempo, uma colega de trabalho quer remover um determinado colaborador que não precisa mais acessar o intervalo.

Para fazer isso, você e sua colega de trabalho leem o mesmo estado inicial para os metadados do intervalo e cada um faz a modificação que quiser nas entradas de ACL do intervalo. Quando você gravar suas modificações no Cloud Storage, os metadados serão atualizados corretamente. Infelizmente, suas alterações são perdidas assim que sua colega de trabalho faz o upload das modificações dela, já que não tinha como levar em consideração sua atualização. Como resultado, a entrada de ACL do seu colaborador é perdida. Ela não poderá acessar seu intervalo e ninguém está ciente do que aconteceu (sem voltar e ver as entradas de ACL).

Como evitar disputas

Você e sua colega de trabalho podem evitar essa disputa adicionando uma condição prévia if-metageneration-match a cada uma das suas operações de gravação. Na condição prévia, vocês usam o número de metageração do intervalo, que faz parte dos metadados recebidos na operação de leitura inicial.

Quando suas modificações adicionam a entrada de ACL, o número de metageração do intervalo é alterado. Agora que as condições prévias estão sendo usadas, quando sua colega de trabalho tenta gravar uma versão das entradas de ACL, o número de metageração do intervalo não corresponde ao número na condição prévia e ela é informada da falha na atualização com um código de resposta 412 Precondition Failed. Ao receber esse código de resposta, sua colega de trabalho pode agir de acordo, executando um novo ciclo de leitura/modificação/gravação usando os metadados atualizados.

Várias tentativas de solicitação

O Cloud Storage é um sistema distribuído. Como as solicitações podem apresentar falha devido a condições de rede ou de serviço, o Google recomenda que você tente novamente com espera exponencial. No entanto, por causa da natureza dos sistemas distribuídos, às vezes essas novas tentativas podem causar um comportamento inesperado.

Imagine este caso: você quer excluir o arquivo file.txt, que está armazenado no Cloud Storage. Depois, você quer adicionar um novo arquivo com o mesmo nome ao Cloud Storage.

Para realizar isso, você emite uma solicitação de exclusão do objeto. No entanto, uma condição de rede, como um roteador intermediário que perde a conectividade temporariamente, impede que a solicitação chegue ao Cloud Storage e você não recebe uma resposta.

Como nenhuma resposta foi recebida para a primeira solicitação, você emite uma segunda solicitação de exclusão do objeto, que é bem-sucedida, e recebe uma resposta de confirmação. Um minuto depois, você decide fazer o upload de um novo file.txt e a operação é bem-sucedida.

Uma disputa surge se o roteador que perdeu a conectividade conseguir recuperá-la subsequentemente e enviar sua solicitação de exclusão original, aparentemente perdida, para o Cloud Storage. Quando a solicitação chega ao Cloud Storage, ela é bem-sucedida porque há um novo file.txt presente. O Cloud Storage envia uma resposta que você não recebe porque seu cliente parou de detectá-lo. Não apenas o novo arquivo é excluído, ao contrário do que você queria, mas também você não está ciente da segunda exclusão.

O diagrama a seguir mostra o que aconteceu:

Como evitar disputas

Para evitar que a situação acima ocorra, o primeiro passo é conseguir os metadados do file.txt para determinar a geração atual. Depois, envie a solicitação de exclusão com uma condição prévia if-generation-match que use o número de geração. O uso da condição prévia garante que apenas o objeto com esse número de geração específico seja excluído, independentemente de quando a solicitação chega ao Cloud Storage ou de quantas vezes ela é enviada. Com a condição prévia if-generation-match, quaisquer tentativas não intencionais de fazer mutação em uma geração diferente de file.txt apresentarão falha com o código de resposta 412 Precondition Failed.

Como interrupções de rede semelhantes podem provocar disputas na solicitação de upload posterior à solicitação de exclusão, é possível evitar muitas delas com uma if-generation-match:0 aplicada à solicitação de upload. Usar essa condição prévia garante que as novas tentativas de upload não gravarão acidentalmente o objeto duas vezes porque a condição prévia permite que a solicitação seja bem-sucedida somente se não houver gerações atuais do objeto.

Com essas condições prévias, você protege seus dados de serem perdidos acidentalmente ao realizar solicitações de exclusão e upload. Isso pode ser visto no diagrama a seguir:

Limitações da if-generation-match:0

A condição prévia if-generation-match:0 não impede que a criação de um objeto ocorra duas vezes quando o primeiro objeto criado é excluído, porque a ausência do objeto não é exclusivamente identificável. Imagine o caso a seguir, em que nenhum dado é perdido, mas você termina com um arquivo que não esperava:

  1. Você começa com uma solicitação GET para os metadados do file.txt para encontrar o número de geração. Na resposta, você descobre que file.txt não existe.

  2. Sabendo disso, você realiza uma solicitação para fazer upload de file.txt com a condição prévia if-generation-match:0, mas a solicitação expira quando um roteador intermediário temporariamente perde a conectividade.

  3. Depois de falhar na primeira vez, você tenta realizar mais uma vez a solicitação de upload, novamente com a condição prévia if-generation-match:0. Dessa vez, a solicitação é bem-sucedida.

  4. Logo depois, você envia uma solicitação para excluir file.txt, que é bem-sucedida.

  5. Se o roteador que perdeu a conectividade se recuperar e enviar sua primeira solicitação de upload para o Cloud Storage, a condição prévia da solicitação ainda será uma correspondência. Por isso, o file.txt é recriado. Com ou sem a condição prévia, inesperadamente é feito o upload do file.txt pela segunda vez.

Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…

Precisa de ajuda? Acesse nossa página de suporte.