Można by pomyśleć, że --link-dest
znalezienie identycznego pliku zadziałałoby we wszystkich przypadkach. Ale nie dzieje się tak, gdy plik istnieje, nawet jeśli plik jest nieaktualny / ma inną zawartość.
To z tego powodu ze strony podręcznika rsync na --link-dest
:
„Ta opcja działa najlepiej podczas kopiowania do pustej hierarchii docelowej, ponieważ rsync traktuje istniejące pliki jako ostateczne (więc rsync nigdy nie szuka w katalogach link-dest, gdy plik docelowy już istnieje )”
Oznacza to, że jeśli y/file
istnieje to samo co źródło i z/file
jest nieaktualne,
rsync -a --del -link-dest=y source:/file z
spowoduje użycie DWÓCH i-węzłów (i dwa razy więcej miejsca na dysku) y/file
i z/file
, które będą miały tę samą zawartość i znaczniki danych.
Natknąłem się na to, ponieważ wykonuję codzienne kopie zapasowe w zasadzie z tym skryptem uruchamianym raz dziennie:
mv $somedaysago $today;
yest=$today; today=`date +%Y%m%d`;
rsync -avPShyH --del --link-dest=../$yest host:/dirs $today
Ponieważ moje kopie zapasowe obejmują do 10 milionów plików, robienie rm -rf $olddir; rsync source:$dir newdir
tego zajęłoby o wiele za dużo czasu (szczególnie, gdy zmienia się tylko 0,5% plików dziennie, powodując usunięcie i utworzenie wpisów 10 milionów katalogów tylko do obsługi 50 tysięcy nowych lub zmienionych plików, co spowodowałoby, że moje kopie zapasowe nie zostały ukończone na następny dzień).
Oto demo sytuacji:
a
jest naszym źródłem, 1
poprzez 4
nasze numerowane kopie zapasowe:
$ mkdir -p 1 2; echo foo > 1/foobar; cp -lrv 1/* 2
`1/foobar' -> `2/foobar'
$ ls -i1 */foobar
1053003 1/foobar
1053003 2/foobar
$ mkdir a; echo quux > a/foobar
$ mv 1 3; rsync -avPhyH --del --link-dest=../2 a/ 3
sending incremental file list
./
foobar
5 100% 0.00kB/s 0:00:00 (xfer#1, to-check=0/2)
sent 105 bytes received 34 bytes 278.00 bytes/sec
total size is 5 speedup is 0.04
$ ls -i1 */foobar
1053003 2/foobar
1053007 3/foobar
1053006 a/foobar
$ mv 2 4; rsync -avPhyH --del --link-dest=../3 a/ 4
sending incremental file list
./
foobar
5 100% 0.00kB/s 0:00:00 (xfer#1, to-check=0/2)
sent 105 bytes received 34 bytes 278.00 bytes/sec
total size is 5 speedup is 0.04
$ ls -il1 */foobar
1053007 -rw-r--r-- 1 math math 5 Mar 30 00:57 3/foobar
1053008 -rw-r--r-- 1 math math 5 Mar 30 00:57 4/foobar
1053006 -rw-r--r-- 1 math math 5 Mar 30 00:57 a/foobar
$ md5sum [34a]/foobar
d3b07a382ec010c01889250fce66fb13 3/foobar
d3b07a382ec010c01889250fce66fb13 4/foobar
d3b07a382ec010c01889250fce66fb13 a/foobar
Teraz mamy 2 kopie zapasowe, a/foobar
które są identyczne pod każdym względem, w tym znacznik czasu, ale zajmują różne i-węzły.
Można by pomyśleć, że byłoby rozwiązanie --delete-before
, które zabija korzyść ze skanowania przyrostowego, ale to też nie pomaga, ponieważ plik nie zostanie usunięty, ale zostanie użyty jako podstawa w przypadku, gdy możliwe jest kopiowanie przyrostowe.
Można jeszcze przypuszczać, że możemy wyłączyć ten żywopłot z kopią przyrostową --whole-file
, ale to nie pomaga algorytmowi, nie ma sposobu na uzyskanie tego, czego chcemy.
Uważam to zachowanie za kolejny błąd w rsync, w którym korzystne zachowanie można interpretować na podstawie starannego wyboru różnych argumentów poleceń, ale pożądany wynik nie jest dostępny.
Rozwiązaniem byłoby niestety być w ruchu z jednego rsync jako operacja atomowa na sucho z -n
, zalogowaniu się, że przetwarzanie dziennika jako wkład do wstępnie ręcznie usunąć wszystkie zmienione pliki, a następnie uruchomiony rsync --link-dest
, aby dostać to, co chcemy - duży kludge w porównaniu do pojedynczego czystego rsync.
Dodatek: próbowano wstępnie połączyć $yesterday
i $today
na serwerze kopii zapasowej przed utworzeniem kopii zapasowej w skrzynkach produkcyjnych z rsync --link-dest=../$yesterday $yesterday/ $today
- ale z takim samym rezultatem - każdy plik, który istnieje w jakikolwiek sposób, nawet o długości 0, nigdy nie zostanie usunięty i przeznaczony dla łącza, zamiast całego nowa kopia zostanie utworzona z sourcedir z nowym i-węzłem i zajmuje więcej miejsca na dysku.
Patrząc na pax(1)
możliwe rozwiązanie poprzedzające tworzenie kopii zapasowych.
--delete-after
jest w porządku, ale nie ma związku z danym problemem. Pliki brakujące w źródle zostaną usunięte po zakończeniu kopiowania. Problem, który wyjaśniam, dotyczy tworzenia kopii zapasowej dzisiaj, która jest identyczna z wczorajszą, ale w stosunku do starego istniejącego nieaktualnego pliku, który nie jest połączony z wczorajszym i-węzłem, ale jest przechowywany jako nowy plik o łącznej powierzchni dwa razy większej niż wczoraj rozważana jest identyczna kopia.
rsnapshot
? Rozważ też napisanie małego skryptu, aby ponownie połączyć „identyczne” pliki. Robię oba na moich systemach.
hardlink(1)
jest wolny (15x wolniejszy niż skanowanie metadanych rsync); pax
jest szybszy, ale przebija głowice dysków twardych, porównując starą kopię zapasową z nową. rsync -n
uzyskanie listy delta oznacza dwukrotne uderzenie w serwery produkcyjne (skanowanie plików 10M ma o wiele większy wpływ niż kopiowanie zmian 50K). Zle wyslam list o opcji w rsync, zezwalajac na to.
--delete-after
w tym scenariuszu użytkowania, co jest z tym nie tak?