Controle de versões de objetos e controle de simultaneidade

Visão geral

Os buckets com controle de versão mantêm versões desatualizadas de objetos, fornecendo uma maneira de cancelar a exclusão de dados que você excluiu acidentalmente ou recuperar versões mais antigas dos seus dados. O controle de versões de um bucket pode ser ativado ou desativado a qualquer momento. Se o controle de versões for desativado, as versões de objetos atuais não serão alteradas, mas o bucket excluirá a versão ativa do objeto sempre que uma nova versão for enviada.

Independentemente de você ter ativado o controle de versão em um intervalo, cada objeto tem dois campos inteiros positivos associados:

  • a geração, que é atualizada quando um novo objeto substitui um existente com o mesmo nome.
  • a metageração, que identifica a geração de metadados. Ele começa em 1; é atualizado sempre que os metadados (por exemplo, ACL ou Content-Type) para uma determinada geração de conteúdo é atualizada; e é redefinido quando o número de geração é alterado.

Desses dois números inteiros, somente a geração é usada ao trabalhar com dados com controle de versão. Tanto a geração quanto a metageneração podem ser usadas com o controle de simultaneidade (discutido em uma seção posterior).

Para trabalhar com o controle de versão de objetos na gsutil, você pode usar uma variação dos URLs de armazenamento que incorporam a geração de objetos, que chamamos de URLs específicos da versão. Por exemplo, o URL do objeto sem versão:

gs://bucket/object

pode ter duas versões, com estes URLs específicos da versão:

gs://bucket/object#1360383693690000
gs://bucket/object#1360383802725000

As seções a seguir discutem como trabalhar com controle de versão e simultaneidade.

Controle de versão de objeto

Você pode ver, ativar e desativar o controle de versão de objeto em um bucket usando os comandos "versioning get" e "versioning set". Exemplo:

gsutil versioning set on gs://bucket

ativará o controle de versão para o intervalo nomeado. Consulte gsutil help versioning para ver mais detalhes.

Para ver todas as versões de objeto em um intervalo com controle de versão, juntamente com as informações de geração.metageneração, use gsutil ls -a:

gsutil ls -a gs://bucket

Também é possível especificar objetos específicos para os quais você quer encontrar os URLs específicos da versão ou usar caracteres curinga:

gsutil ls -a gs://bucket/object1 gs://bucket/images/*.jpg

Os valores de geração formam uma sequência monotonicamente crescente à medida que você cria versões adicionais de objetos. Por isso, a versão mais recente do objeto é sempre a última listada na saída do gsutil ls em um determinado objeto. Por exemplo, se um bucket contém estas três versões de gs://bucket/object:

gs://bucket/object#1360035307075000
gs://bucket/object#1360101007329000
gs://bucket/object#1360102216114000

Em seguida, gs://bucket/object#1360102216114000 é a versão mais recente e gs://bucket/object#130035307075000 é a versão mais antiga disponível.

Se você especificar URLs sem versão com a gsutil, operará apenas na versão ativa de um objeto, por exemplo:

gsutil cp gs://bucket/object ./dir

ou

gsutil rm gs://bucket/object

O mesmo acontece ao usar caracteres curinga como * e **. Eles funcionarão apenas na versão ativa dos objetos correspondentes. Por exemplo, este comando removerá a versão ativa e criará uma versão não atual para cada objeto em um intervalo:

gsutil rm gs://bucket/**

Para operar em uma versão específica do objeto, use um URL específico à versão. Por exemplo, suponha que a saída do comando gsutil ls -a acima seja:

gs://bucket/object#1360035307075000
gs://bucket/object#1360101007329000

Nesse caso, o comando:

gsutil cp gs://bucket/object#1360035307075000 ./dir

recuperará a segunda versão mais recente do objeto.

Os URLs específicos da versão não podem ser o destino do comando gsutil cp (a tentativa de fazer isso resultará em um erro), porque gravar em um objeto com versão sempre cria uma nova versão.

Observe também que algumas conchas tratam "#" como um caractere especial (por exemplo, zsh com a opção estendida). Se você estiver usando um shell que trate "#" como um caractere especial, será necessário colocar o argumento entre aspas, como:

gsutil cp 'gs://bucket/object#1360035307075000' ./dir

Se um objeto tiver sido excluído, ele não será exibido em uma listagem normal da gsutil (ou seja, ls sem a opção -a). Você pode restaurar um objeto excluído executando gsutil ls -a para encontrar as versões disponíveis e, em seguida, copiando um dos URLs específicos da versão para o URL sem versão, por exemplo:

gsutil cp gs://bucket/object#1360101007329000 gs://bucket/object

Quando você faz isso, uma nova versão do objeto é criada, o que gera cobranças adicionais. Para excluir a cópia extra, exclua o objeto específico da versão mais antiga:

gsutil rm gs://bucket/object#1360101007329000

Ou você pode combinar as duas etapas usando o comando gsutil mv:

gsutil mv gs://bucket/object#1360101007329000 gs://bucket/object

Se você remover a versão ativa de um objeto em um intervalo ativado para controle de versão, uma versão não atual será preservada:

gsutil rm gs://bucket/object

Se você remover um URL específico da versão de um objeto (mesmo que seja a versão ativa), essa versão será excluída permanentemente:

gsutil rm gs://bucket/object#1360101007329000

Se você quiser remover todas as versões de um objeto, use a opção gsutil rm -a:

gsutil rm -a gs://bucket/object

Se você quiser remover todas as versões de todos os objetos em um intervalo (e o próprio intervalo), use a opção rm -r (-r sugere a opção -a):

gsutil rm -r gs://bucket

Não há limite para o número de versões mais antigas de um objeto que você criará se continuar fazendo upload para o mesmo objeto em um intervalo com controle de versão. É sua responsabilidade excluir versões além das que você quiser manter.

Como copiar intervalos com controle de versão

É possível copiar dados entre dois buckets com controle de versão usando um comando como:

gsutil cp -r -A gs://bucket1/* gs://bucket2

Quando executado usando buckets com controle de versão, esse comando faz com que todas as versões de objetos sejam copiadas. As cópias feitas em gs://bucket2 terão números de geração diferentes (uma vez que uma nova geração é atribuída quando a cópia do objeto é feita), mas a ordem de classificação do objeto permanecerá consistente. Por exemplo, gs://bucket1 pode conter:

% gsutil ls -la gs://bucket1 10  2013-06-06T02:33:11Z
53  2013-02-02T22:30:57Z  gs://bucket1/file#1359844257574000  metageneration=1
12  2013-02-02T22:30:57Z  gs://bucket1/file#1359844257615000  metageneration=1
97  2013-02-02T22:30:57Z  gs://bucket1/file#1359844257665000  metageneration=1

e depois da cópia, gs://bucket2 pode conter:

% gsutil ls -la gs://bucket2
53  2013-06-06T02:33:11Z  gs://bucket2/file#1370485991580000  metageneration=1
12  2013-06-06T02:33:14Z  gs://bucket2/file#1370485994328000  metageneration=1
97  2013-06-06T02:33:17Z  gs://bucket2/file#1370485997376000  metageneration=1

As versões do objeto estão na mesma ordem (como pode ser visto pela mesma sequência de tamanhos nas duas listagens), mas os números de geração (e carimbos de data / hora) são mais recentes em gs:// bucket2.

Controle de simultaneidade

Se você estiver criando um aplicativo usando o Cloud Storage, talvez seja necessário ter cuidado com o controle de simultaneidade. Normalmente, a gsutil não é usada para essa finalidade, mas é possível escrever scripts em torno da gsutil que executam o controle de simultaneidade.

Suponha que você queira implementar um sistema de "atualização gradual" usando o gsutil, em que um job periódico calcula alguns dados e os envia para a nuvem. Em cada execução, o job começa com os dados calculados a partir da última execução e calcula um novo valor. Para tornar esse sistema robusto, você precisa ter várias máquinas em que o job pode ser executado, o que aumenta a possibilidade de duas execuções simultâneas tentarem atualizar um objeto ao mesmo tempo. Isso leva à seguinte condição de corrida:

  • job 1 calcula o novo valor a ser gravado
  • o job 2 calcula o novo valor a ser gravado
  • o job 2 grava o novo valor
  • o job 1 grava o novo valor

Nesse caso, o valor que o job 1 leu não está mais atualizado no momento em que é gravado para gravar o objeto atualizado. A gravação neste ponto resultaria em uma versão desatualizada ou, dependendo do aplicativo, corrompido. para a área de transferência do sistema.

Para evitar isso, você pode encontrar o nome específico da versão do objeto que foi criado e usar as informações contidas nesse URL para especificar um cabeçalho x-goog-if-site-match em um comando gsutil cp subsequente. Você pode fazer isso em duas etapas. Primeiro, use a opção gsutil cp -v no momento do upload para obter o nome específico da versão do objeto que foi criado, por exemplo:

gsutil cp -v file gs://bucket/object

pode gerar:

Created: gs://bucket/object#1360432179236000

Você pode extrair o valor de geração desse objeto e, em seguida, construir um comando gsutil posterior da seguinte maneira:

gsutil -h x-goog-if-generation-match:1360432179236000 cp newfile \
    gs://bucket/object

Esse comando solicita que o Cloud Storage tente fazer upload de newfile, mas falhe na solicitação se a geração de newfile ativo no momento do upload não corresponder ao especificado.

Se o comando usado atualizar metadados de objeto, você precisará encontrar a metageração atual de um objeto. Para fazer isso, use as opções gsutil ls -a e -l. Por exemplo, o comando .

gsutil ls -l -a gs://bucket/object

gera algo como:

  64  2013-02-12T19:59:13Z  gs://bucket/object#1360699153986000  metageneration=3
1521  2013-02-13T02:04:08Z  gs://bucket/object#1360721048778000  metageneration=2

Com essas informações, você pode usar o seguinte comando para solicitar a configuração da ACL na versão mais antiga do objeto, de modo que o comando falhe, a menos que essa seja a versão atual dos dados + metadados:

gsutil -h x-goog-if-generation-match:1360699153986000 -h \
  x-goog-if-metageneration-match:3 acl set public-read \
  gs://bucket/object#1360699153986000

Sem adicionar esses cabeçalhos, a atualização simplesmente substitui a ACL atual. Por outro lado, o comando "gsutil acl ch" usa esses cabeçalhos automaticamente, porque executa um ciclo de leitura, modificação e gravação para editar ACLs.

Se você quiser testar como as gerações e as metagerações funcionam, tente o seguinte. Primeiro, faça o upload de um objeto. Em seguida, use gsutil ls -l -a para listar todas as versões do objeto, junto com a metageneração de cada versão; Em seguida, faça o upload do objeto novamente e repita o gsutil ls -l -a. Você verá duas versões de objeto, cada uma com metageneration=1. Agora, tente definir a ACL e execute novamente a gsutil ls -l -a. Você verá que a geração mais recente do objeto tem metageneration=2.

Para mais informações

Para mais detalhes sobre como usar o controle de versões e as condições prévias, consulte https://cloud.google.com/storage/docs/object-versioning.