Aws-cli: Problemas de sincronização AWS S3

Criado em 17 jan. 2014  ·  41Comentários  ·  Fonte: aws/aws-cli

Houve alguns problemas com relação ao comando sync , particularmente no caso de sincronização do S3 ( s3 -> local ). Eu gostaria de tentar resumir os problemas conhecidos, bem como algumas propostas de opções possíveis, e dar às pessoas a oportunidade de compartilhar qualquer feedback que possam ter.

Visão geral do comportamento de sincronização

O comportamento de sincronização pretende ser um eficiente cp ; copie apenas os arquivos da origem para o destino que sejam diferentes. Para fazer isso, precisamos ser capazes de determinar se um arquivo em s3 / local é ou não diferente. Para fazer isso, usamos dois valores:

  • Tamanho do arquivo (de stat 'ing o arquivo localmente e da chave Size em uma resposta ListObjects )
  • Hora da última modificação (mtime do arquivo local e a chave LastModified em uma resposta ListObjects )

Como um aparte, usamos a operação ListObjects porque obtemos até 1000 objetos retornados em uma única chamada. Isso significa que estamos limitados às informações que vêm de uma resposta ListObjects que é LastModified, ETag, StorageClass, Key, Owner, Size .

Agora, dados o tamanho do arquivo dos arquivos remotos e locais e os tempos da última modificação, tentamos determinar se o arquivo é diferente. O tamanho do arquivo é fácil, se os tamanhos dos arquivos forem diferentes, então sabemos que os arquivos são diferentes e precisamos sincronizar o arquivo. No entanto, a hora da última modificação é mais interessante. Embora o mtime do arquivo local seja um mtime verdadeiro, o LastModified time de ListObjects é realmente o horário em que o objeto foi carregado. Então imagine este cenário:

aws s3 sync local/ s3://bucket/
sleep 10
aws s3 sync s3://bucket local/

Após o primeiro comando de sincronização ( local->s3 ), os arquivos locais terão um mtime de 0 e o conteúdo em s3 terá um tempo de LastModified de 10 (usando deslocamentos relativos). Quando executamos o segundo comando aws s3 sync, que está sincronizando de s3 para local, primeiro faremos a verificação do tamanho do arquivo. Neste caso, os tamanhos dos arquivos são os mesmos, portanto, examinamos as últimas verificações de hora modificadas. Nesse caso, eles são diferentes (local == 0, s3 == 10). Se estivéssemos fazendo uma comparação de igualdade estrita, porque os tempos da última modificação são diferentes, sincronizaríamos desnecessariamente os arquivos de s3 para local. Portanto, podemos dizer que se os tamanhos dos arquivos forem iguais e o tempo da última modificação em s3 for maior (mais recente) do que o arquivo local, então não sincronizamos. Este é o comportamento atual.

No entanto, isso criará um problema se o arquivo remoto for atualizado fora da banda (por meio do console ou algum outro SDK) e o tamanho permanecer o mesmo. Se executarmos aws s3 sync s3://bucket local/ , não sincronizaremos o arquivo remoto, embora devamos fazer isso.

Soluções Potenciais

Abaixo estão as soluções potenciais.

  1. Altere as verificações de tempo para uma comparação de igualdade estrita. Se os horários forem diferentes, nós sincronizamos. Isso causa o problema de aws s3 sync local s3://bucket && aws s3 s3://bucket local sincronizar arquivos desnecessariamente. No entanto, quando baixamos um arquivo, definimos a hora do arquivo para corresponder à hora de LastModified, de forma que se você executasse aws s3 sync s3://bucket local _novamente_, não sincronizaria nenhum arquivo.
  2. Modifique os horários mt locais quando os arquivos são carregados para coincidir com o horário da última modificação de S3. Em seguida, mudaríamos as verificações de tempo de arquivo para uma verificação estrita de igual / diferente. A única desvantagem dessa abordagem é se as pessoas esperam ou não que mexamos com o tempo de seus arquivos (eu não esperaria).
  3. Adicione metadados personalizados a cada objeto. Isso poderia potencialmente adicionar alguma combinação de mtime local, md5, para que também possamos comparar as somas de verificação md5, se quisermos. No entanto, teríamos que mudar de ListObjects para HeadObject para cada solicitação. Isso exigiria 1000 vezes mais chamadas de API e tornaria a sincronização mais lenta no caso de muitos arquivos pequenos. No caso de estado estacionário, desde que possamos fazer o HeadObjects mais rápido do que fazer o download / upload deles, não deve haver um problema. Uma desvantagem dessa abordagem é que outras ferramentas não adicionarão esses metadados, o que tornaria a interoperabilidade com outras ferramentas mais difícil (se um objeto não tiver os metadados, provavelmente apenas sincronizaremos).
  4. Mantenha um cache ETag local. Poderíamos associar o nome do arquivo a um ETag / mtime local / hora da última modificação / md5 do arquivo. No lado positivo, ainda usamos ListObjects, portanto, obtemos 1000 objetos de volta por vez. A desvantagem é que cada cliente terá que manter um cache. Esta também é a solução mais complexa em termos de implementação.

Se houver alguma outra solução potencial que eu deixei de fora, entre em contato.

automation-exempt feature-request s3 s3md5 s3sync

Comentários muito úteis

Empurrando isso de volta!

Todos 41 comentários

Em vez de armazenar o cache de ETags fornecidas pelo servidor, a ETag pode ser calculada no lado do cliente? Então seria quase como fazer verificações md5, mas usando dados disponíveis na resposta ListObjects. Contanto que o algoritmo ETag não dependa do estado do lado do servidor ...

Você achou :-)

- Seb

Em 17 de janeiro de 2014, às 16:26, Jeff Waugh < notificaçõ[email protected] [email protected] > escreveu:

Ah: https://forums.aws.amazon.com/thread.jspa?messageID=203510&state=hashArgs%23203510

-
Responda a este e-mail diretamente ou visualize-o em Gi tHubhttps: //github.com/aws/aws-cli/issues/599#issuecomment -32668382.

Amazon EU Societe a responsabilite limitee, 5 Rue Plaetis, L - 2338 Luxembourg, RCS Luxembourg n B 101818, capital social: EUR 37.500. Autorização d estabelecimento en qualite de commercante n 104408.

Sim, não podemos calcular com segurança a ETag para uploads de várias partes, caso contrário, seria uma ótima solução.

Você poderia adicionar um novo sinalizador (ou 2) para o comportamento do tempo? Talvez --check-timestamps para a opção 1 e --update-local-timestamps para a opção 2. Dessa forma, o usuário pode especificar uma verificação mais robusta para alterações e aceitar as consequências ao mesmo tempo.

Sim, acho que adicionar sinalizadores para as opções 1 e 2 seria uma abordagem razoável. Uma preocupação potencial é que o comportamento padrão (sem opções especificadas) tem casos em que sync não se comporta como seria de se esperar, mas não tenho certeza se alterar o comportamento padrão para qualquer uma dessas opções é uma coisa boa aqui, dadas as possíveis compensações que estaríamos fazendo.

@jamesls Estou usando o comando sync para implantar um site estático gerado.

Com a versão atual, estou reenviando todos os arquivos a cada sincronização porque o mtime muda quando o site é regenerado, mesmo que o conteúdo não seja alterado.

Para meus propósitos (e imagino um bom número de outras pessoas usando essa ferramenta fabulosa para fazer upload de seus sites estáticos), sincronizar via ETag, conforme sugerido em # 575, seria o mais incrível, mas, dada a minha leitura desse problema, não parece ser uma opção.

Exceto que, para fins de sites estáticos, uma verificação apenas de comprimento (embora talvez um pouco perigosa) funcionaria.

Outra opção seria desativar os uploads de várias partes e usar # 575 - veríamos uma grande economia imediatamente.

Eu encontrei o problema inverso. Eu mudei um arquivo no S3 que tem o mesmo tamanho, mas o carimbo de data / hora mais recente e a sincronização do S3 não o puxa para baixo

aws s3 sync s3: // bucket / path / dir

Olhando os dados no S3 ... acho que é por causa de problemas de fuso horário.
As propriedades mostram um tempo de

Última modificação: 21/02/2014 10:50:33 AM

Mas os cabeçalhos HTTP mostram

Última modificação: Sex, 21 de fevereiro de 2014 15:50:33 GMT

Observe que a propriedade Última modificação não mostra o fuso horário?

Como meu comando s3 sync está sendo executado em um servidor diferente com fuso horário diferente daquele em que coloquei o arquivo, ele pensa que o arquivo está no passado e não o puxa.

Tive que mudar para s3 cp para ter certeza de que receberia todos os arquivos

Acho que, como primeiro passo, devemos implementar o argumento --size-only . Não resolve o problema no caso geral, mas para certos cenários ajudará e é fácil de entender / explicar, particularmente o caso de uso referenciado acima com sites estáticos sendo sincronizados para s3.

Acho que a sincronização deve ter uma opção de sempre sincronizar os arquivos se o arquivo a ser sincronizado for mais recente que o destino. Estamos sincronizando arquivos da máquina A para a S3 e depois da S3 para a máquina B. Se o tamanho de um arquivo não mudar (mas o conteúdo, sim), este arquivo não alcançará a máquina B. Este comportamento é interrompido. Não me importo se sincronizo muitos arquivos, mas os arquivos alterados nunca devem ser deixados de fora.

De acordo com meu post anterior, "newer" precisa levar em consideração o fuso horário também.
Atualmente não é assim se você enviar um arquivo para o S3 de um fuso horário e, em seguida, sincronizar de outro, ele não detectará corretamente que o arquivo é mais recente.

@jamesls Além do argumento --size-only, eu estaria interessado em usar um argumento --name-only. Ou seja, não verifique o tamanho do arquivo ou a hora da última modificação. Simplesmente copie os arquivos existentes na origem, mas não no destino. Em nosso cenário, sincronizamos do s3 para o local e, depois de fazer o download de um arquivo, não esperamos que ele mude no s3. Se essa opção resultar em menos operações em nosso sistema de arquivos local (nfs), pode haver uma melhoria de desempenho.

@jamesls Deve --size-only et al estar disponível em 1.3.6?

Meu representante de suporte da AWS para o caso 186692581 diz que encaminhou minha sugestão para você.
Pensei em postar aqui de qualquer maneira para comentar:

Acho que uma solução simples seria introduzir um fator de difusão.
Se normalmente não demorasse mais de 5 minutos para a cópia local -> S3,
em seguida, use um fator de difusão de 10 minutos nas comparações de tempo subsequentes.
Trate os tempos relativos dentro de 10 minutos como iguais.
Se o tempo S3 for mais de 10 minutos mais recente, sincronize de S3 -> local.
Talvez adicione "--fuzz = 10m" como uma opção.

@jamesls @ adamsb6
Não seria https://github.com/aws/aws-cli/pull/575 uma boa opção pelo menos para arquivos carregados de uma única parte?

Se você verificar o formato ETAG do arquivo no S3, poderá diferir se o upload do arquivo foi simples ("ETAG =" MD5 Hash ") ou multiparte (ETAG =" MD5 Hash "-" Número de partes "). pode comparar todos os arquivos MD5 locais com seu ETAG e, no caso de um arquivo ter sido carregado como multiparte, você pode ignorá-lo.

Temos um cliente que tem muitos clipes de vídeo em certas pastas em um S3 Bucket, que são sincronizados com instâncias ec2 em todas as regiões da AWS. Todos os arquivos são carregados como parte única.
No momento, temos um problema devido ao s3cmd, que em alguns casos alguns arquivos estão corrompidos. Se fizermos uma sincronização completa novamente, teremos 14 TB de tráfego que serão cobrados.

Nosso problema: Os arquivos corrompidos têm exatamente o mesmo tamanho do arquivo original no s3 e, devido a carimbos de data / hora incorretos no s3cmd, não podemos usar as opções mencionadas acima. Nesse caso, --compare-on-etag seria uma ótima solução para evitar a sincronização de todos os arquivos novamente.

Mesmo para a sincronização normal, a opção --compare-on-etag seria ótima, se você tiver apenas uma parte dos arquivos carregados, porque o aws s3 sync sincronizará apenas os arquivos alterados.

Acabei de passar a maior parte de 3 horas tentando encontrar as permissões mínimas necessárias para usar o comando sync. O erro que estava recebendo era:
A client error (AccessDenied) occurred when calling the ListObjects operation: Access Denied

Quando realmente o erro deveria ter sido:
A client error (AccessDenied) occurred when calling the ListBucket operation: Access Denied

Um item de ajuda que mostra uma tabela com as permissões mínimas para cada comando seria _muito_ útil.

404 parece uma _realmente boa ideia_, visto que até ler isso pensei que a sincronização já fizesse isso.

Editar: Para esclarecer, adicione o comportamento do tipo rsync ao "aws s3 sync". Parece que o problema relatado não é exatamente o que eu inicialmente entendi.

Como o último AWS-CLI-bundle.zip não contém a correção implementada acima, fiz um clone do git. Posso ver o novo código em uma pasta chamada "personalizações". No entanto, não está claro para mim como criar um comando aws-cli usando esse código. Eu tenho que executar make-bundle?

Sim. Eu uso as seguintes etapas para instalá-lo em novos servidores (Ubuntu):

git clone https://github.com/andrew512/aws-cli.git
cd aws-cli
pip install -r requirements.txt
sudo python setup.py install

OK.
Eu vejo o código modificado na versão 1.3.18.
Ele aceita meu parâmetro --exact-timestamps.
Achei que o pacote de download mais recente que eu tinha instalado anteriormente era 1.3.21.

O controle de versão confiável realmente só se aplica aos lançamentos oficiais da AWS. Eu fiz um bifurcação do repositório em 1.3.18, então essa é a versão que ele reportará, mas já está algumas versões desatualizadas, com 1.3.22 sendo a mais recente neste momento. Esperançosamente, a AWS aceita a solicitação pull e inclui o recurso em versões oficiais futuras. Tem sido muito valioso para nós e ajuda a resolver um comportamento padrão bastante questionável.

@ andrew512 Desculpe pela demora. Acho que o PR que você enviou é uma boa ideia e é muito útil ter feedback do cliente sobre quais aws s3 sync mudanças funcionam para eles e quais não funcionam. Vou dar uma olhada em breve.

Eu acho ... para aqueles de nós que não se importam com as solicitações de cabeçalho, a comparação no MD5 deve ser uma opção. Eu (em segundo lugar) votaria no --compare-on-etag porque atualizo apenas de um servidor para o S3 - e, portanto, um repositório MD5 local não é um problema para mim. MAS eu definitivamente acho que precisamos ter algo. Do jeito que está, NUNCA tenho certeza de que meus repositórios local e S3 são iguais. Onde estamos no status de algo assim?

@janngobble +1

Nosso caso de uso é que temos esses arquivos em um repositório git e eles são arquivos de configuração, portanto, nem a data de modificação nem o tamanho do arquivo realmente funcionam, então gostaríamos de ver uma opção md5 real para aqueles que podem lidar com as implicações de desempenho.

Isso ocorre porque quando você faz check-out de um repositório git, a data de modificação do arquivo é quando o arquivo é gravado. Além disso, o tamanho do arquivo não funciona porque a alteração do arquivo pode ser algo como:

foo="bar"

para

foo="baz"

para que o tamanho do arquivo não seja alterado.

@jamesls Por que você não pode usar o método aqui para calcular o md5 para uploads de várias partes? Funcionou para mim

Olá,
Eu também tenho esse problema bem descrito com foo = "bar" / foo = "baz".
Eu uso o balde S3 para a implantação do meu aplicativo e todos os servidores sincronizam a partir do S3 quando uma implantação é concluída. Tive poucas vezes o problema de um operador> = alterado para <= em um arquivo não sincronizado devido a esse bug e para mim o comando de sincronização não é muito confiável devido a esse bug. O tamanho é o mesmo, mas o conteúdo é diferente, o arquivo deve ser sincronizado.
Não tenho nenhum conselho específico sobre como fazer isso, desculpe por isso, mas estou apenas expondo meu caso de uso :)

Vai entender, eu me deparei com o mesmo problema durante o desenvolvimento do node-ftpsync. Presumi que a AWS teria alguma solução mágica para resolver isso.

  1. É uma merda, mas é necessário. Uma comparação mtime.equals é a maneira mais rápida / suja de detectar mudanças. Infelizmente, os horários dos relógios entre os hosts locais e remotos precisam ser normalizados antes de serem comparados.

Provavelmente é uma boa ideia 'difundi-lo' (ou seja, arredondando para os 10 minutos mais próximos), como sugeriu @ngbranitsky . Fazer isso em node.js é uma dor, mas em python deve ser tão fácil quanto truncar os últimos bits usando um AND bit a bit.

  1. Pode funcionar, com efeitos colaterais. Eu explorei essa opção possivelmente alterando o mtime local para coincidir com o valor MDTM do servidor (isto é, FTP mtime). Infelizmente, as implementações do servidor FTP raramente são compatíveis com RFC e podem não incluir MDTM. Caso em questão, a sincronização de arquivos por FTP é uma droga porque não há garantia de que o cliente e o servidor estejam completos.

Como a AWS não tem esse problema, você também deve considerar como o mtime é usado no host local. Ao alterar o valor mtime em cada sincronização, você vai acionar um evento de atualização em massa se houver transpiladores assistindo a esses arquivos? Existem outros armazenamentos de metadados que usam mtime como uma métrica para medir as alterações de arquivo?

  1. É a melhor opção se você não levar em consideração o custo. Chamadas de API adicionais geram um custo para os usuários (ou seja, como em custo monetário). O MD5 só será necessário para acréscimos de arquivos e arquivos alterados, mas para usuários que armazenam MUITOS arquivos pequenos e / ou arquivos que mudam com frequência, ele pode somar.
  2. É de longe a melhor opção para garantir a consistência, mas também a maior dificuldade de implementação.

Sei que essa não é uma solução geral, mas seria muito bom ver o seguinte comportamento opcional para sincronização, imo. É como se não fosse. 4 no OP, modificado apenas para melhor desempenho.

  1. Um cache local de etags é mantido para o sistema de arquivos local e, opcionalmente, o servidor, se nenhuma alteração fora da banda nos arquivos do depósito puder ser garantida pelo usuário.
  2. Ao sincronizar local-> servidor, uma primeira passagem usando comparação mtime / size contra o banco de dados de cache é usada para determinar possíveis mudanças nos arquivos locais. (Isso é feito para evitar a recomputação desnecessária do md5, que é caro em relação ao fstat.)
  3. Para o conjunto potencialmente alterado, o md5-> etag é recalculado. Se o arquivo for realmente diferente, ele será carregado. O banco de dados do cache local é atualizado em qualquer caso para evitar recomputar o md5 em arquivos locais.

O caso de uso aqui é sincronizar blocos de mapas da web, dos quais <1% normalmente mudam diariamente. A exceção é quando ocorrem exclusões nos dados de origem que alteram a cobertura espacial dos blocos, o que exige que todo o conjunto de blocos do mapa seja regenerado.

O problema é o grande volume envolvido. Vejo a discussão sobre grandes uploads de várias partes como um caso de uso, mas não sobre muitos arquivos pequenos individuais. Quantos? Temos ~ 2 milhões atualmente, mas pode ficar muito pior. Por exemplo, no nível de zoom 16, o mundo tem 1 << 16 x 1 << 16, ou 65536 x 65536 blocos ou ~ 4b.

As opções atuais são:

  1. Sincronize com o aws-cli normalmente, recarregando tudo.

    1. Este é o mais rápido no geral.

    2. Infelizmente, muitos uploads desnecessários ocorrerão.

  2. Sincronize com s3cmd, que usa md5 (etag) em vez de mtimes. Infelizmente:

    1. Ele sempre recalcula o md5 para cada arquivo local toda vez que é executado, o que é relativamente caro e exige muito IOPS.

    2. Ele tem um único thread e é limitado pela memória e, embora sempre lento, é especialmente lento para fazer upload de muitos arquivos individuais, particularmente em casos de latência mais alta para outras regiões S3.

Eu poderia escrever código C sem muita dificuldade que iria percorrer um caminho de diretório, atualizar um banco de dados de cache sqlite3 local e construir a fila de set / upload potencialmente alterada. Infelizmente, não tenho experiência em Python e não posso enviar isso como uma solicitação pull para um comportamento de sincronização opcional.

Eu não sei o quão comum é o caso de uso "muitos arquivos pequenos, garantido nenhuma mudança fora de banda, local-> servidor apenas".

Acho pouco intuitivo e perigoso que a sincronização trate uma diferença nos últimos tempos modificados como um motivo para atualizar, mesmo se um arquivo mais recente na origem estiver substituindo um mais antigo no destino. Esse comportamento estranho deve ser documentado.

Também relacionado a # 404.

Que tal adicionar um único sinalizador --sync-if autodocumentado em vez do número crescente de opções não autodocumentáveis? (por exemplo, --size-only , --exact-timestamps . Posso aplicar esses dois ao mesmo tempo? Por que tenho que ler a documentação / experimentá-los para descobrir isso?).

O --sync-if poderia ter uma lista de opções:

--sync-if newer-timestamp,different-md5,different-timestamp,different-size,deleted,...

O usuário pode especificar um ou mais (lista separada por vírgulas) e se o arquivo atender a algum dos critérios, será atualizado (carregado / excluído) no destino.

Isso esclareceria muito o comportamento, especialmente se você mencionou na documentação que o comportamento padrão é: --sync-if different-timestamp,different-size .

Lendo este problema, não consigo descobrir se esse comportamento de sincronização ainda foi corrigido.

Eu quero algo que funcione tão simplesmente quanto rsync -avz para sincronizar minha compilação local com as do servidor.

Eu estava usando aws s3 sync , mas porque tenho um arquivo que é grande (um arquivo de ajuda que é um filme) e minha etapa de compilação cria todos os arquivos do novo, copia todos os arquivos sempre e é desnecessariamente lento para atualizar o site.

Então comecei a usar --size-only para acelerar. Infelizmente, isso me mordeu na bunda recentemente. Eu havia renomeado um arquivo e a etapa de construção inclui uma lista de arquivos no arquivo service-worker.js para que o sw saiba o que armazenar em cache. Infelizmente, os nomes dos arquivos novos e antigos tinham o mesmo comprimento, então ele não atualizou o arquivo service-worker.js e continuou dando um 404 para o nome do arquivo antigo. Levei um bom tempo para descobrir o que estava acontecendo.

Parece que este é um problema resolvido em outros ambientes - ou seja, por rsync - embora, tbh, eu provavelmente seja um pouco ignorante dos desafios apresentados em fazer algo assim no S3. Enfim, tendo sido mordido por isso recentemente, estou procurando outros clientes, mas eles parecem ter dependências que não estou interessado em adotar apenas para essa funcionalidade.

Seria ótimo ter uma opção de sincronização etag. Eu sei que existem cenários em que ele falha, mas para mim seria muito valioso.

Bom Dia!

Estamos encerrando esse problema aqui no GitHub, como parte de nossa migração para o UserVoice para solicitações de recursos envolvendo o AWS CLI.

Isso nos permitirá fornecer os recursos mais importantes para você, tornando mais fácil pesquisar e mostrar suporte para os recursos que você mais gosta, sem diluir a conversa com relatórios de bug.

Como uma cartilha rápida do UserVoice (se ainda não for familiar): depois que uma ideia é postada, as pessoas podem votar nas ideias e a equipe do produto responderá diretamente às sugestões mais populares.

Importamos solicitações de recursos existentes do GitHub - Pesquise este problema lá!

E não se preocupe, esse problema ainda existirá no GitHub para o bem da posteridade. Como é uma importação somente de texto da postagem original para o UserVoice, ainda teremos em mente os comentários e a discussão que já existem aqui sobre o problema do GitHub.

O GitHub continuará sendo o canal para relatar bugs.

Mais uma vez, esse problema agora pode ser encontrado pesquisando o título em: https://aws.uservoice.com/forums/598381-aws-command-line-interface

- A equipe de SDKs e ferramentas da AWS

Esta entrada pode ser encontrada especificamente no UserVoice em: https://aws.uservoice.com/forums/598381-aws-command-line-interface/suggestions/33168808-aws-s3-sync-issues

Evitando problemas com o github? Isso parece um erro ...

Concordou. Parece mais o método que a Microsoft usa para julgar a importância / impacto dos problemas, mas acho bastante irritante.

Com base no feedback da comunidade, decidimos retornar as solicitações de recursos para problemas do GitHub.

Empurrando isso de volta!

Para o topo!

a comparação do md5 seria ótimo, eu também acrescentaria que seria útil gerar o md5 no upload ou download. isso poderia ser armazenado em nosso próprio banco de dados e ajudaria a determinar se a sincronização é necessária por meio de nosso banco de dados para limitar as solicitações.

@jamesls Você poderia comentar este assunto, por favor?
https://github.com/aws/aws-cli/issues/4460

Esta página foi útil?
0 / 5 - 0 avaliações