Я делаю несколько ночных и еженедельных зеркал часто используемых репозиториев для локальной сети. В нескольких случаях кто-то пытался выполнить обновление во время выполнения 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 задача или вроде)? Если да, то вы, вероятно, используете для этого специального пользователя ОС, верно? Таким образом, решение может быть вместо простого копирования:
Обратной стороной является то, что во время процесса синхронизации (не знаю, сколько времени это займет) целевой каталог не будет доступен. Вам нужно решить, все ли здесь нормально.