Zachowanie się programu rsync przy wciąż zapisywanym pliku?

13

Jeśli Apache jest w trakcie pisania dużego pliku, a na tym pliku działa zadanie rsync cron, to czy rsync próbuje skopiować plik?

Przykład

  • Apache-1: zapisano duży plik /var/www.
  • Apache-2: Clone of Apache-1. Co pięć minut cron uruchamia rsync, aby się /var/wwwzsynchronizować.
Louis Waweru
źródło

Odpowiedzi:

21

Jeśli Apache pisze jakiś plik w jednym miejscu i nie ukończył go zapisywać, a następnie rsync uruchamia, rsyncskopiuje wszystko, co tam siedzi.

Oznacza to, że jeśli Apache ma do czynienia z plikiem 5 MB, zostanie zapisany tylko 2 MB i zostanie rsyncuruchomiony, częściowy plik 2 MB zostanie skopiowany. Wygląda więc na to, że plik jest „uszkodzony” na serwerze docelowym.

W zależności od rozmiaru używanych plików możesz użyć --inplaceopcji w, rsyncaby wykonać następujące czynności:

Ta opcja zmienia sposób, w jaki rsync przesyła plik, gdy dane pliku wymagają aktualizacji: zamiast domyślnej metody tworzenia nowej kopii pliku i przenoszenia go na miejsce po zakończeniu, rsync zapisuje zaktualizowane dane bezpośrednio do miejsca docelowego plik.

Zaletą tego jest to, że jeśli plik 5 MB ma tylko 2 MB skopiowane przy pierwszym uruchomieniu, następne uruchomienie pobierze 2 MB i będzie kontynuować kopiowanie pliku, aż pełne 5 MB będzie na swoim miejscu.

Negatywne jest to, że może stworzyć sytuację, w której ktoś uzyskuje dostęp do serwera WWW podczas kopiowania pliku, a następnie zobaczy plik częściowy. Moim zdaniem rsyncnajlepiej sprawdza się w domyślnym zachowaniu buforowania „niewidzialnego” pliku, a następnie natychmiastowego przeniesienia go na miejsce. Ale --inplacejest dobry w scenariuszach, w których duże pliki i ograniczenia przepustowości mogą przeszkadzać w łatwym kopiowaniu dużego pliku z pierwszego.

Powiedziałeś, że to stwierdzasz; nacisk jest mój:

Co pięć minut cron uruchamia rsync…

Więc zakładam, że masz jakiś skrypt bash do zarządzania tym zadaniem cron? Cóż, sprawa jest rsyncwystarczająco inteligentna, aby skopiować tylko pliki, które należy skopiować. A jeśli masz skrypt, który jest uruchamiany co 5 minut, wydaje się, że starasz się unikać rsyncna siebie nawzajem, jeśli idzie szybciej. Oznacza to, że jeśli uruchamiasz go co minutę, istnieje ryzyko, że jeden lub więcej rsyncprocesów będzie nadal działać z powodu rozmiaru pliku lub szybkości sieci, a następny proces będzie po prostu z nim konkurował; warunki wyścigowe.

Jednym ze sposobów uniknięcia tego jest zawinięcie całego rsyncpolecenia w skrypt bash, który sprawdza blokadę pliku; poniżej znajduje się szkielet skryptu bash, którego używam do takich przypadków.

Zauważ, że niektórzy ludzie zalecą używanie, flockale ponieważ flocknie jest zainstalowany na niektórych systemach, których używam - i często przeskakuję między Ubuntu (który ma) i Mac OS X (który nie ma) - używam tej prostej struktury bez żadnego poważnego problemu:

LOCK_NAME="MY_GREAT_BASH_SCRIPT"
LOCK_DIR='/tmp/'${LOCK_NAME}.lock
PID_FILE=${LOCK_DIR}'/'${LOCK_NAME}'.pid'

if mkdir ${LOCK_DIR} 2>/dev/null; then
  # If the ${LOCK_DIR} doesn't exist, then start working & store the ${PID_FILE}
  echo $$ > ${PID_FILE}

  echo "Hello world!"

  rm -rf ${LOCK_DIR}
  exit
else
  if [ -f ${PID_FILE} ] && kill -0 $(cat ${PID_FILE}) 2>/dev/null; then
    # Confirm that the process file exists & a process
    # with that PID is truly running.
    echo "Running [PID "$(cat ${PID_FILE})"]" >&2
    exit
  else
    # If the process is not running, yet there is a PID file--like in the case
    # of a crash or sudden reboot--then get rid of the ${LOCK_DIR}
    rm -rf ${LOCK_DIR}
    exit
  fi
fi

Chodzi o to, że ten rdzeń - tam, gdzie ja mam echo "Hello world!"- jest sercem twojego skryptu. Reszta jest w zasadzie mechanizmem blokującym / logiką opartą na mkdir. Dobre wyjaśnienie tego pojęcia znajduje się w tej odpowiedzi :

mkdir tworzy katalog, jeśli jeszcze nie istnieje, a jeśli tak, ustawia kod wyjścia. Co ważniejsze, robi to wszystko w jednej akcji atomowej, dzięki czemu idealnie nadaje się do tego scenariusza.

Tak więc w przypadku twojego rsyncprocesu poleciłbym użycie tego skryptu, po prostu zmieniając echopolecenie na twoje rsync. Zmień też LOCK_NAMEna coś takiego, RSYNC_PROCESSa wtedy możesz zacząć.

Teraz, gdy Twój rsyncskrypt jest zawinięty, możesz ustawić zadanie crona do uruchamiania co minutę bez ryzyka warunków wyścigowych, w których dwa lub więcej rsyncprocesów walczy o to samo. Umożliwi to zwiększenie prędkości lub rsyncaktualizacji, co nie wyeliminuje problemu przesyłania częściowych plików, ale pomoże przyspieszyć cały proces, aby w pewnym momencie można było poprawnie skopiować cały plik.

JakeGould
źródło
2
Dzięki za wskazanie możliwości uruchomienia wielu rsyncs, nie myślałem o tym. Skrypt brzmi świetnie. Próbowałem po prostu zrozumieć, jak zsynchronizować stronę z równoważeniem obciążenia z rsync, co wydaje się je łagodzić. Cudowny bonus. Nadal czuję, że może to niewłaściwe podejście ... ale zobaczmy :)
Louis Waweru
@Louis Nie ma za co! Ponadto, jeśli chcesz zachować synchronizację folderów w oparciu o natychmiastowe zmiany plików, zdecydowanie polecam skorzystanie z / dostosowanie lsyncd. Pozwala mieć „gorące foldery”, które naprawdę zwracają uwagę na aktywność w nich, a następnie działają na tych plikach po wprowadzeniu zmian. Używam rsyncdużo, jak opisano w mojej odpowiedzi, ale używam lsyncddo przypadków, które wymagają niezwiązanej z cronem / bardziej natychmiastowej formy działania.
JakeGould
3

Tak - plik może być uszkodzony, jeśli rsync odczytuje plik w tym samym czasie, w którym zapisywany jest plik.

Możesz spróbować: /unix//a/2558

Możesz także napisać skrypt za pomocą lsof:

lsof /path/to file

Kod wyjścia 0 oznacza, że ​​plik jest w użyciu, a kod wyjścia 1 oznacza, że ​​na tym pliku nie ma żadnej aktywności.

rebelshrug
źródło
Nie rozumiem, dlaczego plik zostanie uszkodzony, jeśli rsync go po prostu czyta
orestisf