Я делаю несколько ночных и еженедельных «зеркал» часто используемых репозиториев для локальной сети. В нескольких случаях кто-то пытался выполнить обновление во время выполнения rsync и терпел неудачу, потому что ожидаемые файлы еще не все там.
Можно ли выполнить rsync так, чтобы все измененные файлы появлялись только с правильными именами после завершения? Я знаю, что rsync использует временные .hidden файлы во время каждой передачи, но могу ли я как-то отложить переименование до ее завершения? В качестве альтернативы кажется, что я могу использовать опцию --backup для перемещения всех изменений в один каталог и атомарного перемещения их после, но я бы хотел, чтобы эта функция работала в обратном порядке, чем сейчас.
Ответ 1
Вы можете использовать опцию --link-dest=. По сути, вы создадите новую папку, все файлы будут жестко привязаны к новой папке. Когда все будет готово, вы можете просто поменять имена папок местами и удалить старую. В Linux невозможно сделать это на 100% атомарно, так как нет поддержки ядра/VFS. Однако, обмен именами – это всего лишь 2 системных вызова, так что на это должно уйти меньше 1 секунды. Это возможно только на Darwin (MAC/OSX) с помощью системного вызова exchangedata на файловой системе HFS.
Ответ 2
Я делаю нечто подобное с резервным копированием rsync (на диск) и столкнулся с той же проблемой из-за того, что демон обновляет файлы во время выполнения резервного копирования. В отличие от многих программ, rsync имеет множество различных кодов ошибок (см. man-страницу). Интерес представляют два из них:
23 -- частичная передача из-за ошибки
24 -- неполная передача из-за исчезновения исходных файлов.
Когда rsync выполняет передачу и сталкивается с одной из этих ситуаций, он не останавливается сразу. Он пропускает их и продолжает передачу файлов, которые он может передать. В конце он выдает код возврата.
Поэтому, если вы получили ошибку 23/24, просто запустите rsync повторно. Последующие запуски будут проходить намного быстрее, обычно просто передавая недостающие файлы из предыдущего запуска. В конце концов вы получите (или должны получить) чистый результат. Что касается атомарности, я использую каталог «tmp» во время переноса. Затем, когда rsync работает чисто, я переименовываю его (атомарно) в <дата>. Я также использую опцию --link-dest, но я использую ее для сохранения дельта-резервных копий (например, --link-dest=yesterday для ежедневных операций).
Хотя я сам его не использовал, параметр --partial-dir=DIR может предотвратить загромождение каталога резервного копирования скрытыми файлами. Убедитесь, что DIR находится в той же файловой системе, что и каталог резервного копирования, чтобы переименования были атомарными. Хотя я делаю это на perl, я написал сценарий, который суммирует то, что я говорил, с немного большей детализацией/точностью для вашей конкретной ситуации. Он написан на tcsh-подобном синтаксисе (непроверенном и немного грубом), но рассматривайте его как псевдокод для написания собственного сценария на bash, perl, python по своему усмотрению. Обратите внимание, что в нем нет ограничения на повторные попытки, но вы можете легко добавить это в соответствии с вашими пожеланиями.
#!/bin/tcsh -f
# repo_backup -- резервное копирование репозиториев, даже если они изменились
# use_tmp -- использовать временную директорию назначения
# use_partial -- использовать частичную директорию
# use_delta -- делать дельта-резервное копирование
# установить имя удаленного сервера ...
set remote_server="..."
# каталог на сервере для резервных копий
set backup_top="/path_to_backup_top"
set backup_backups="$backup_top/backups"
# установите параметры rsync ...
set rsync_opts=(...)
# не допускать загромождения резервной копии частичными файлами
set server_partial=${remote_server}:$backup_top/partial
if ($use_partial) then
set rsync_opts=($rsync_opts --partial-dir=$server_partial)
endif
# сделать дельта-резервное копирование
if ($use_delta) then
set latest=(`ssh ${remote_server} ls $backup_backups | tail -1`)
# получить последнюю версию
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)
# получить список всего для резервного копирования
# установите любой нужный вам параметр
cd /local_top_directory
set transfer_list=(.)
# используйте любой формат по своему усмотрению
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
# выполнить передачу
rsync $rsync_opts $transfer_list $server_transfer
set code=$status
# запуск был чистым
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 - некоторая ошибка
if ($code == 23) then
continue
endif
# partial - некоторые файлы исчезли
if ($code == 24) then
continue
endif
echo "фатальная ошибка ..."
exit(1)
end
Ответ 3
Является ли синхронизация зеркал автоматической (задача cron или что-то подобное)? Если да, то вы, вероятно, используете для этого выделенного пользователя ОС, я прав? Тогда решение может быть таким: вместо простого копирования:
Установите разрешения на каталог назначения так, чтобы только rsync мог получить к нему доступ.
Приступите к синхронизации.
Измените разрешения целевого каталога, чтобы остальные могли снова получить к нему доступ.
Недостатком является то, что во время процесса синхронизации (не знаю, сколько времени это займет) целевой каталог будет недоступен. Вы должны сами решить, нормально ли это.
Ответ 4
Не уверен, что это вам поможет, но...
Если вы не против копирования всего набора данных каждый раз и если вы можете использовать симлинки для ссылки на целевой каталог, то вы должны передавать состояние rsync во временный каталог, а затем поменять местами (rename()) старые и новые симлинки атомарно, примерно так:
% 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» для каталога, на который ссылается симлинк, перед попыткой чтения, иначе они рискуют загрузить некоторые части кода/данных из старой копии.
Linux