Могу ли я безопасно читать файл, который добавляется другим процессом?

Если процесс A копирует файлы в какое-либо местоположение, loc и процесс B регулярно копирует файлы из loc в другое место, может ли B прочитать файл, который в настоящее время находится в процессе копирования в loc by A?

Я использую Ubuntu Linux 12.04, если это важно.


Фоновая информация: я хочу постоянно создавать резервные копии кластера PostgreSQL. PostgreSQL предоставляет для этого архивирование WAL. Он работает путем вызова базы данных сценария, который копирует завершенный файл WAL в какое-то место для резервного копирования.

Я хочу, чтобы другой процесс регулярно копировал резервные файлы WAL на другой сервер. Если в настоящее время файл WAL копируется в базу данных, может ли второй процесс по-прежнему читать файл, не запускаясь в какое-либо условие EOF до того, как файл будет скопирован в целом?

Другими словами: могу ли я сделать следующее без синхронизации между A и B?

AB cp pg_xlog/some_wal_file /backup/ scp /backup/* user@remote-machine:/backups/ 

Я считаю, что лучше всего обеспечить, чтобы процесс B копировал только файлы, которые были полностью переданы процессом A. Один из способов сделать это – использовать комбинацию cp и mv в процессе A, поскольку в процессе mv используется rename системный вызов (при условии, что файлы находятся в одной файловой системе), который является атомарным. Это означает, что с точки зрения процесса B файлы появляются в полностью сформированном состоянии.

Одним из способов реализации этого было бы иметь partial каталог внутри вашего каталога /backup который игнорируется процессом B. Для процесса A вы можете сделать что-то вроде:

 file="some_wal_file" cp pg_xlog/"$file" /backup/partial mv /backup/partial/"$file" /backup 

А для процесса B (используя bash ):

 shopt -s extglob scp /backup/!(partial) user@remote-machine:/backups/ 

Хотя программа, которую вы, вероятно, хотите изучить, как для процесса A, так и для процесса B, является rsync . rsync создает частичные файлы и атомарно перемещается на место по умолчанию (хотя обычно частичные файлы являются скрытыми файлами, а не находятся в определенном каталоге). Rsync также будет избегать передачи файлов, которые ему не нужны, и имеет специальный алгоритм дельта для передачи только соответствующих частей файлов, которые необходимо обновить по сети ( rsync должен быть установлен в обоих местоположениях, хотя передача по-прежнему проходит через ssh по умолчанию). Использование rsync для процесса A:

 rsync -a --partial-dir=/backup/partial pg_xlog/some_wal_file /backup/ 

Для процесса B:

 rsync -a --exclude=/partial/ /backup/ user@remote-machine:/backups/ 

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

Общим способом избежать этой ошибки является копирование файла под временным именем, а затем его переименование:

 dest=$(TMPDIR=/backup mktemp) trap 'rm -f "$dest"' INT HUP ERR cp -p pg_xlog/some_wal_file "$dest" mv "$dest" "/backup/some_wal_file" 

У потребителя устраивает не копирование временных файлов. В вашем сценарии вы можете сделать это, сделав его точечным файлом – используйте dest=$(TMPDIR=/backup mktemp .XXXXXXXXXX) выше. Простым способом является вызов rsync вместо cp , поскольку rsync использует эту стратегию по умолчанию:

 rsync -a pg_xlog/some_wal_file /backup/ 

На шаге B обязательно удалите эти временные файлы, например:

 rsync -a --exclude='/.*' /backup/ user@remote-machine:/backups/ 

Если вы не хотите полагаться на файлы точек, вы можете использовать промежуточную папку. Перенос файла из каталога в другой является атомарным, если два каталога находятся в одной и той же файловой системе.

 mkdir -p /backup/incoming cp -p pg_xlog/some_wal_file /backup/incoming/ mv /backup/incoming/some_wal_file /backup/ 
 rsync -a --exclude=/staging /backup/ user@remote-machine:/backups/