Como o RSYNC funciona?

marcelo_devsres

Marcelo Andrade

Posted on December 17, 2023

Como o RSYNC funciona?

Em um determinado projeto esta semana, comentei que o clássico programa rsync, na verdade, não faz análise de checksum por padrão na hora de identificar que arquivos transferir. Isso surpreendeu algumas pessoas!

Então acredito que compartilhar este "mini-lab" com explicações ajude você a entender o básico do funcionamento do rsync!

RSYNC é assunto para PHD!

O Tridgell , um dos criadores do rsync, descreveu o funcionamento do programa em sua tese de Ph.D. em 1999, e você pode lê-la aqui.

Como nós não somos acadêmicos aqui e queremos apenas usar a ferramenta, vamos decompor duas partes importantes do funcionando do rsync:

  1. Como ele determina que arquivos precisam ser enviados para o destino;
  2. Quais as partes do arquivo foram alteradas e precisam ser enviadas para o destino

São duas coisas totalmente diferentes, mas que geram a confusão que eu entendo que muitas pessoas fazem.

Analisando apenas a parte 2., o rsync é capaz de, a partir de um arquivo com determinado tamanho, dividí-lo em pedaços, calcular um hash para cada pedaço, e identificar qual pedaço precisa ser enviado. Isso é excepcionalmente útil na hora de transferir um arquivo muito grande mas que é 99,9% igual na origem ou no destino. Um exemplo seriam arquivos de logs gerados de maneira progressiva ou massas de dados incompletas que foram transferidas parcialmente por interrupção do serviço. Nessas horas, você pode contar com o rsync para transferir apenas o incremento de 1 ou 2 GiB, e não os 10TiB exatamente iguais do arquivo.

Mas essa operação de checksum é processada após o rsync entender que o arquivo precisa ser enviado. Isso não quer dizer que o rsync executa um checksum em cada arquivo para saber que ele precisa ser alterado.

É extremamente importante não confundir as duas coisas, são etapas diferentes!

Que arquivos precisam ser enviados para o destino?

A resposta está no man rsync

Rsync finds files that need to be transferred using a "quick check" algorithm (by default) that looks for files that have changed in size or in last-modified time. Any changes in the other preserved attributes (as requested by options) are made on the destination file directly when the quick check indicates that the file's data does not need to be updated.

Então, o "algoritmo" padrão de detecção nem merece um nome bonito, chamado de quick check. Ele observa mudanças no tamanho do arquivo ou na data e hora da última modificação (ou mtime).

Algumas pessoas chamam esse algoritmo de checkrq ou lquick ou ainda pior lquickcheckrq por causa desta página:

Screenshot do Linux Die

Isso inclusive uma grande piada interna de alguns usuários mais atentos:

Screeshot da rsync manpage

O lq do 'lqquick' e o rq do 'checkrq' são, na verdade, o "left quote" e "right quote" da manpage mal transpostos do formato man para html! Logo, por favor, não chame o algoritmo de "checkrq" ou você corre o risco de alguém gargalhar na sua frente!

Mas a parte importante é essa: rsync, por padrão, não executa checksum nos arquivos para decidir se ele precisa transferí-lo (inteiro ou seus pedaços) ou não. Ele executa uma simples análise de timestamp e tamanho.

Por quê? Porque é muito mais rápido e extremamente efetivo! Qual a chance de um arquivo com mesmo tamanho e mesmo mtime ser diferente em duas origens que comumente se sincronizam?

Enganando o rsync:

Vamos fazer um lab.

mkdir -p /tmp/testersync/{origem,destino}

echo "O rsync nao faz checksum por padrao" > /tmp/testersync/origem/arquivo
Enter fullscreen mode Exit fullscreen mode

Se, um tempo depois, você criar um arquivo igual no destino:

echo "O rsync nao faz checksum por padrao" > /tmp/testersync/destino/arquivo
Enter fullscreen mode Exit fullscreen mode

E usar o rsync:

rsync -avi /tmp/testersync/origem/ /tmp/testersync/destino
sending incremental file list
>f..t...... arquivo
Enter fullscreen mode Exit fullscreen mode

Esse t na saída do log mostra que ele notou que o timestamp está diferente.

Se você mudar o tamanho da frase:

echo "Claro que o rsync faz checksum por padrao, oras" > /tmp/testersync/destino/arquivo
Enter fullscreen mode Exit fullscreen mode

Agora o rsync vai falar que ele mudou não só o timestamp mas o tamanho:

rsync -avi  /tmp/testersync/origem/ /tmp/testersync/destino
sending incremental file list
>f.st...... arquivo
Enter fullscreen mode Exit fullscreen mode

Agora vamos tentar enganar o rsync. Vou criar o arquivo com a mesma frase, mas toda em maiúscula:

cat /tmp/testersync/origem/arquivo | tr '[a-z]' '[A-Z]' > /tmp/testersync/destino/arquivo
Enter fullscreen mode Exit fullscreen mode

Do ponto de vista computacional, claramente eles são diferentes:

find /tmp/testersync -type f -exec sha256sum '{}' \+
f27d88d52759a4b07c077f34c230c47b94ea88b6640fc47b44d562243966eb7e  /tmp/testersync/destino/arquivo
47c6ec4243572212a13d657637a62975a1007cf5881668cf7d02d370956dc3b3  /tmp/testersync/origem/arquivo
Enter fullscreen mode Exit fullscreen mode

O tamanho é o mesmo:

find /tmp/testersync -type f -printf '%P: %s\n'
destino/arquivo: 36
origem/arquivo: 36
Enter fullscreen mode Exit fullscreen mode

Mas se você usar o rsync, ele vai retransferir o arquivo porque o mtime é diferente, e isso é um grande indicador que algo mudou no arquivo:

find /tmp/testersync -type f -printf '%P: %t\n'
destino/arquivo: Sun Dec 17 14:45:20.4011998470 2023
origem/arquivo: Sun Dec 17 14:43:11.4812008470 2023
Enter fullscreen mode Exit fullscreen mode

Então, se você executar o rsync, ele vai substituir o arquivo:

rsync -avi /tmp/testersync/origem/ /tmp/testersync/destino
sending incremental file list
>f..t...... arquivo

find /tmp/testersync -type f -exec sha256sum '{}' \+
47c6ec4243572212a13d657637a62975a1007cf5881668cf7d02d370956dc3b3  /tmp/testersync/destino/arquivo
47c6ec4243572212a13d657637a62975a1007cf5881668cf7d02d370956dc3b3  /tmp/testersync/origem/arquivo
Enter fullscreen mode Exit fullscreen mode

Só que isso é bem diferente de executar análise de checksum, que em larga escala, pode ser muito ineficiente.

Vamos enganar o rsync. Primeiro recrio o arquivo diferente:

cat /tmp/testersync/origem/arquivo | tr '[a-z]' '[A-Z]' > /tmp/testersync/destino/arquivo
Enter fullscreen mode Exit fullscreen mode

Depois eu altero os horários dele para ficarem iguais ao da origem (conhece essa função do comando touch?):

touch /tmp/testersync/destino/arquivo -r /tmp/testersync/origem/arquivo
Enter fullscreen mode Exit fullscreen mode

Como o rsync enxerga o arquivo agora?

find /tmp/testersync -type f -printf '%P:Tamanho %s, Mtime %t\n'
destino/arquivo:Tamanho 36, Mtime Sun Dec 17 14:43:11.4812008470 2023
origem/arquivo:Tamanho 36, Mtime Sun Dec 17 14:43:11.4812008470 2023
Enter fullscreen mode Exit fullscreen mode

Do ponto de vista do rsync, eles agora são iguais. Duvida? Vamos executá-lo:

rsync -avi /tmp/testersync/origem/ /tmp/testersync/destino
sending incremental file list

sent 85 bytes  received 12 bytes  194.00 bytes/sec
total size is 36  speedup is 0.37
Enter fullscreen mode Exit fullscreen mode

Pelo log, você pode ver que ele não transferiu nada, e o checksum segue diferente:

find /tmp/testersync -type f -exec sha256sum '{}' \+
f27d88d52759a4b07c077f34c230c47b94ea88b6640fc47b44d562243966eb7e  /tmp/testersync/destino/arquivo
47c6ec4243572212a13d657637a62975a1007cf5881668cf7d02d370956dc3b3  /tmp/testersync/origem/arquivo
Enter fullscreen mode Exit fullscreen mode

Usando checksum para identificar arquivos diferentes

Neste tipo de situação - que geralmente é improvável de acontecer, mas não necessariamente impossível, você precisa da flag -c:

rsync -avic /tmp/testersync/origem/ /tmp/testersync/destino
sending incremental file list
>fc........ arquivo


find /tmp/testersync -type f -exec sha256sum '{}' \+
47c6ec4243572212a13d657637a62975a1007cf5881668cf7d02d370956dc3b3  /tmp/testersync/destino/arquivo
47c6ec4243572212a13d657637a62975a1007cf5881668cf7d02d370956dc3b3  /tmp/testersync/origem/arquivo
Enter fullscreen mode Exit fullscreen mode

Agora sim, eu garanto que origem e destino são exatamente iguais.

Conclusão

Se você quiser garantir que a origem e o destino são iguais, você precisa modificar o comportamento padrão do rsync usando a flag -c ou --checksum.

Se você está trabalhando com arquivos que usam algum tipo de empacotamento binário de data (basicamente qualquer coisa), a chance desse tipo de situação passar batido é extremamente baixa.

Mas quando você trabalha com muitos arquivos textos com formatos e layouts parecidos e com filesystems extravagantes que podem não expor o mtime adequadamente, você pode cair nessa armadilha.

Em uma era em que a leitura dinâmica e o aprendizado resumido estilo TL;DR segue em voga, muitas vezes vale a pena você parar para entender com calma as ferramentas que você usa para evitar cair em armadilhas no futuro!

💖 💪 🙅 🚩
marcelo_devsres
Marcelo Andrade

Posted on December 17, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related