Назад | Перейти на главную страницу

rsync, чтобы все изменения появлялись атомарно

Я делаю несколько ночных и еженедельных зеркал часто используемых репозиториев для локальной сети. В нескольких случаях кто-то пытался выполнить обновление во время выполнения rsync и терпел неудачу, потому что ожидаемые файлы еще не все.

Можно ли сделать rsync так, чтобы все измененные файлы появляются только с правильными именами по завершении? Я знаю, что rsync использует временные .hidden файлы во время каждой передачи, но могу ли я отложить переименование до тех пор, пока оно не будет как-то завершено?

В качестве альтернативы кажется, что я мог бы использовать параметр --backup, чтобы переместить все изменения в один каталог и затем атомарно перемещать их, но я бы хотел, чтобы эта функция работала в обратном порядке, как сейчас.

Я использую Linux, чего бы он ни стоил.

Я делаю нечто подобное с rsync резервные копии [на диск], и я столкнулся с той же проблемой из-за того, что демон обновляет файлы во время резервного копирования.

В отличие от многих программ, rsync имеет много различные коды ошибок [см. нижнюю часть справочной страницы]. Интересны два:

23 - частичный перенос из-за ошибки
24 - частичный перенос из-за исчезнувших исходных файлов

Когда rsync выполняет передачу и сталкивается с одной из этих ситуаций, он не останавливается сразу же. Он пропускает и продолжает файлы, которые он жестяная банка перевод. В конце представлен код возврата.

Итак, если вы получите ошибку 23/24, просто перезапустите rsync. Последующие запуски будут происходить намного быстрее, обычно просто переносятся недостающие файлы из предыдущего запуска. В конце концов, вы получите [или должны получить] чистый пробег.

Что касается атомарности, я использую во время передачи каталог "tmp". Затем, когда rsync run очищается, я переименовываю его [атомарно] в <date>

Я также использую --link-dest вариант, но я использую его для хранения дельта-резервных копий (например, --link-dest=yesterday на день)

Хотя сам я им не пользовался, --partial-dir=DIR может не дать скрытым файлам загромождать каталог резервных копий. Убедитесь, что DIR находится в той же файловой системе, что и ваш резервный каталог, поэтому переименования будут атомарными.

Пока я делаю это на perl, я написал сценарий, который резюмирует то, что я сказал, с немного большей детализацией / точностью для вашей конкретной ситуации. Он имеет синтаксис, похожий на tcsh, [непроверенный и немного грубый], но рассматривайте его как псевдокод для написания собственного bash, perl, python сценарий по вашему выбору. Обратите внимание, что у него нет ограничений на количество повторных попыток, но вы можете легко добавить его по своему желанию.

#!/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

Вы можете использовать --link-dest= вариант. Обычно вы создаете новую папку, все файлы жестко связаны с новой папкой. Когда все будет сделано, вы можете просто поменять местами имена папок и удалить старую.

В Linux невозможно сделать это на 100% атомарно, так как для этого нет поддержки ядра / VFS. Однако замена имен на самом деле находится всего в двух системных вызовах, поэтому для ее завершения потребуется менее 1 секунды. Это возможно только в Darwin (MAC / OSX) с системным вызовом exchangengedata в файловых системах HFS.

Не уверен, что это поможет вам, но ...

Если вы не против копировать весь набор данных каждый раз и если вы можете использовать символические ссылки для ссылки на целевой каталог, тогда вы сможете выполнить rsync все во временный каталог, а затем поменять местами (переименовать ()) старые и новые символические ссылки атомарно, например:

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

который работает

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

и дает

current -> new_data

Даже для того, чтобы это сработало, любые клиенты, пытающиеся читать из этой настройки, должны cd в каталог, на который ссылается символическая ссылка, перед попыткой чтения, в противном случае они рискуют загрузить некоторые части кода / данных из старой копии, а некоторые из новой.

Зеркало синхронизируется автоматически (a cron задача или вроде)? Если да, то вы, вероятно, используете для этого специального пользователя ОС, верно? Таким образом, решение может быть вместо простого копирования:

  1. Установите разрешения для целевого каталога так, чтобы только rsync сможет получить к нему доступ.
  2. Продолжите синхронизацию.
  3. Измените разрешения цели (безоговорочно), чтобы другие могли снова получить к ней доступ.

Обратной стороной является то, что во время процесса синхронизации (не знаю, сколько времени это займет) целевой каталог не будет доступен. Вам нужно решить, все ли здесь нормально.