rsync diretório para que todas as alterações apareçam atomicamente

7

Eu faço alguns espelhos noturnos e semanais de repositórios usados com frequência para a rede local. Em algumas ocasiões, alguém tentou fazer uma atualização enquanto o rsync estava acontecendo e falhou porque os arquivos esperados não estão todos lá ainda.

É possível fazer um rsync de tal forma que todos arquivos alterados apareçam apenas com os nomes corretos na conclusão? Eu sei que o rsync usa arquivos .hidden temporários enquanto cada transferência está em andamento, mas eu posso adiar as renomeações até que ela esteja terminada de alguma forma?

Como alternativa, parece que eu poderia usar a opção --backup para mover todas as alterações para um diretório e movê-las atomicamente depois, mas gostaria que o recurso funcionasse ao contrário do que ele faz agora.

Estou no Linux pelo seu valor.

    
por mikebabcock 07.12.2015 / 18:23

4 respostas

4

Você pode usar a opção --link-dest= . Basicamente você criaria uma nova pasta, todos os arquivos são hard-linked para o novo. Quando tudo estiver pronto, basta trocar os nomes das pastas e remover o antigo.

É impossível fazer isso 100% atômico no Linux, já que não há suporte a kernel / VFS para ele. No entanto, trocar os nomes é na verdade apenas 2 syscalls de distância, então deve demorar menos de 1 segundo para ser concluído. É possível apenas no Darwin (MAC / OSX) com a chamada do sistema exchangedata nos sistemas de arquivos HFS.

    
por 07.12.2015 / 18:31
4

Eu faço algo semelhante com rsync backups [em disco] e encontrei o mesmo problema devido a um daemon que atualiza arquivos enquanto o backup está em execução.

Ao contrário de muitos programas, o rsync tem muitos códigos de erro diferentes [Veja a parte inferior da página de manual]. De interesse são dois:

23 -- partial transfer due to error
24 -- partial transfer due to vanished source files

Quando o rsync está fazendo uma transferência e encontra uma dessas situações, ele não pára imediatamente. Ele salta e continua com os arquivos que ele pode transferir. No final, apresenta o código de retorno.

Portanto, se você receber o erro 23/24, execute novamente o rsync. As execuções subseqüentes serão muito mais rápidas, geralmente apenas transferindo os arquivos ausentes da execução anterior. Eventualmente, você terá [ou deverá obter] uma corrida limpa.

Quanto a ser atômico, eu uso um diretório "tmp" durante a transferência. Então, quando o rsync run estiver limpo, eu o renomeio [atomicamente] para <date>

Eu também uso a opção --link-dest , mas uso isso para manter backups delta (por exemplo, --link-dest=yesterday para diárias)

Embora eu não tenha usado eu mesmo, o --partial-dir=DIR pode impedir que os arquivos ocultos atravancem o diretório de backup. Certifique-se de que o DIR esteja no mesmo sistema de arquivos que seu diretório de backup, para que as renomeações sejam atômicas

Enquanto faço isso em perl, escrevi um script que resume o que venho dizendo com um pouco mais de detalhe / precisão para sua situação particular. Está na sintaxe tcsh-like, [não testada e um pouco difícil], mas trate-a como pseudo-código para escrever seu próprio script bash , perl , python como você escolher. Observe que ele não tem limite para novas tentativas, mas você pode adicioná-lo com bastante facilidade, de acordo com seus desejos.

#!/bin/tcsh -f
# repo_backup -- backup repos even if they change
#
# use_tmp -- use temporary destination directory
# use_partial -- use partial directory
# use_delta -- make delta backup

# set remote server name ...
set remote_server="..."

# directory on server for backups
set backup_top="/path_to_backup_top"
set backup_backups="$backup_top/backups"

# set your rsync options ...
set rsync_opts=(...)

# keep partial files from cluttering backup
set server_partial=${remote_server}:$backup_top/partial
if ($use_partial) then
    set rsync_opts=($rsync_opts --partial-dir=$server_partial)
endif

# do delta backups
if ($use_delta) then
    set latest=('ssh ${remote_server} ls $backup_backups | tail -1')

    # get latest
    set delta_dir="$backup_backups/$latest"

    if ($#latest > 0) then
        set rsync_opts=($rsync_opts --link-dest=${remote_server}:$delta_dir)
    endif
endif

while (1)
    # get list of everything to backup
    # set this to whatever you need
    cd /local_top_directory
    set transfer_list=(.)

    # use whatever format you'd like
    set date='date +%Y%m%d_%H%M%S'

    set server_tmp=${remote_server}:$backup_top/tmp
    set server_final=${remote_server}:$backup_backups/$date

    if ($use_tmp) then
        set server_transfer=$server_tmp
    else
        set server_transfer=$server_final
    endif

    # do the transfer
    rsync $rsync_opts $transfer_list $server_transfer
    set code=$status

    # run was clean
    if ($code == 0) then
        # atomically install backup
        if ($use_tmp) then
            ssh ${remote_server} mv $backup_top/tmp $backup_backups/$date
        endif
        break
    endif

    # partial -- some error
    if ($code == 23) then
        continue
    endif

    # partial -- some files disappeared
    if ($code == 24) then
        continue
    endif

    echo "fatal error ..."
    exit(1)
end
    
por 08.12.2015 / 00:22
1

Não tenho certeza se isso vai ajudar você, mas ...

Se você não se importar em copiar todo o conjunto de dados a cada vez e se puder usar links simbólicos para se referir ao diretório de destino, poderá rsync tudo em um diretório temporário e swap ( renomear () ) o velho e o novo symlinks atomicamente, assim:

% mkdir old_data new_data
% ln -s old_data current
% ln -s new_data new
% strace mv -T new current

que é executado

rename("new", "current") = 0

e dá

current -> new_data

Mesmo que isso funcione, qualquer cliente que tente ler desta configuração deve cd no diretório referenciado pelo symlink antes de tentar qualquer leitura, caso contrário, eles correm o risco de carregar algumas partes do código / dados da cópia antiga e novo.

    
por 07.12.2015 / 20:25
1

As sincronizações de espelhamento são automáticas (uma tarefa cron ou semelhante)? Em caso afirmativo, você provavelmente usa um usuário do sistema operacional dedicado para isso, estou certo? Então a solução poderia ser, em vez de simplesmente copiar:

  1. Defina as permissões do diretório de destino para que somente o rsync possa acessá-lo.
  2. Prossiga com a sincronização.
  3. Altere as permissões do alvo (incondicionalmente) para que os outros possam acessá-lo novamente.

A desvantagem é que, durante o processo de sincronização (não sei quanto tempo leva), o diretório de destino não estará acessível. Você precisa decidir se está tudo bem aqui.

    
por 07.12.2015 / 20:52