Puoi usare il --link-dest=
opzione. Fondamentalmente creeresti una nuova cartella, tutti i file sono collegati a quella nuova. Quando tutto è finito, puoi semplicemente scambiare i nomi delle cartelle e rimuovere quello vecchio.
È impossibile farlo al 100% atomico in Linux poiché non esiste un supporto kernel/VFS per questo. Tuttavia, lo scambio dei nomi richiede in realtà solo 2 chiamate di sistema, quindi dovrebbe essere necessario meno di 1 secondo per completarlo. È possibile solo su Darwin (MAC/OSX) con la chiamata di sistema exchangedata sui filesystem HFS.
Faccio qualcosa di simile con rsync
backup [su disco] e ho riscontrato lo stesso problema a causa di un demone che aggiorna i file mentre il backup è in esecuzione.
A differenza di molti programmi, rsync ne ha molti diversi codici di errore [Vedere la parte inferiore della pagina man]. Di interesse sono due:
23 -- trasferimento parziale per errore
24 -- trasferimento parziale dovuto a file sorgente scomparsi
Quando rsync sta eseguendo un trasferimento e incontra una di queste situazioni, non si ferma immediatamente. Salta e continua con i file che può trasferimento. Alla fine presenta il codice di ritorno.
Quindi, se ricevi l'errore 23/24, esegui nuovamente rsync. Le esecuzioni successive andranno molto più velocemente, di solito solo trasferendo i file mancanti dall'esecuzione precedente. Alla fine, otterrai [o dovresti ottenere] una corsa pulita.
Per quanto riguarda l'essere atomico, durante il trasferimento utilizzo una directory "tmp". Quindi, quando l'esecuzione di rsync è pulita, la rinomino [atomicamente] in <date>
Uso anche il --link-dest
opzione, ma la uso per mantenere i backup delta (ad es. --link-dest=yesterday
per giornaliero)
Anche se non l'ho usato personalmente, il file --partial-dir=DIR
può impedire ai file nascosti di ingombrare la directory di backup. Assicurati che DIR sia sullo stesso filesystem della tua directory di backup in modo che le rinominazioni siano atomiche
Mentre lo faccio in perl, ho scritto uno script che riassume ciò che ho detto con un po' più di dettaglio/precisione per la tua situazione particolare. È in sintassi simile a tcsh, [non testato e un po' approssimativo], ma trattalo come uno pseudo-codice per scrivere il tuo bash
, perl
, python
script come si sceglie. Nota che non ha limiti ai tentativi, ma puoi aggiungerlo abbastanza facilmente, secondo i tuoi desideri.
#!/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