GNU/Linux >> Znalost Linux >  >> Linux

Jak transakčně zkopírovat soubor?

rsync dělá tuto práci. Dočasný soubor je O_EXCL vytvořeno ve výchozím nastavení (zakázáno pouze v případě, že používáte --inplace ) a poté renamed přes cílový soubor. Použijte --ignore-existing aby nepřepsal B, pokud existuje.

V praxi jsem s tím nikdy nezaznamenal žádné problémy na ext4, zfs nebo dokonce NFS připojení.


Nebojte se, noclobber je standardní funkcí.


Ptali jste se na NFS. Tento druh kódu se pravděpodobně pod NFS rozbije, protože se kontroluje noclobber zahrnuje dvě samostatné operace NFS (zkontrolujte, zda soubor existuje, vytvořte nový soubor) a dva procesy ze dvou samostatných klientů NFS se mohou dostat do konfliktu, kdy oba uspějí (oba ověřují, že B.part ještě neexistuje, pak oba pokračují k úspěšnému vytvoření, v důsledku toho se navzájem přepisují.)

Ve skutečnosti není třeba provádět obecnou kontrolu, zda souborový systém, do kterého zapisujete, bude podporovat něco jako noclobber atomově nebo ne. Můžete zkontrolovat typ souborového systému, zda je to NFS, ale to by byla heuristika a ne nutně záruka. Souborové systémy jako SMB/CIFS (Samba) budou pravděpodobně trpět stejnými problémy. Souborové systémy vystavené pomocí FUSE se mohou, ale nemusí chovat správně, ale to většinou závisí na implementaci.

Možná lepší přístup je vyhnout se kolizi v B.part krok, použitím jedinečného názvu souboru (prostřednictvím spolupráce s jinými agenty), takže se nemusíte spoléhat na noclobber . Jako součást názvu souboru byste mohli například zahrnout své jméno hostitele, PID a časové razítko (+případně náhodné číslo.) Vzhledem k tomu, že na hostiteli by měl v daný okamžik běžet jeden proces pod konkrétním PID, mělo by to zaručit jedinečnost.

Takže buď jeden z:

test -f B && continue  # skip already existing
unique=$(hostname).$$.$(date +%s).$RANDOM
cp A B.part."$unique"
# Maybe check for existance of B again, remove
# the temporary file and bail out in that case.
mv B.part."$unique" B
# mv (rename) should always succeed, overwrite a
# previously copied B if one exists.

Nebo:

test -f B && continue  # skip already existing
unique=$(hostname).$$.$(date +%s).$RANDOM
cp A B.part."$unique"
if ln B.part."$unique" B ; then
    echo "Success creating B"
else
    echo "Failed creating B, already existed"
fi
# Both cases require cleanup.
rm B.part."$unique"

Takže pokud máte spor mezi dvěma agenty, oba budou pokračovat v operaci, ale poslední operace bude atomická, takže buď B existuje s úplnou kopií A, nebo B neexistuje.

Velikost závodu můžete zmenšit opětovnou kontrolou po kopii a před mv nebo ln provozu, ale stále je tam malý závod. Ale bez ohledu na spor by měl být obsah B konzistentní, za předpokladu, že se jej oba procesy snaží vytvořit z A (nebo kopie z platného souboru jako původu.)

Všimněte si, že v první situaci s mv , když závod existuje, vyhraje poslední proces, protože rename(2) atomicky nahradí existující soubor:

Pokud nová cesta již existuje, bude atomicky nahrazen, takže neexistuje žádný bod, ve kterém by se jiný proces pokoušel o přístup k nové cestě zjistí, že chybí. [...]

Pokud nová cesta existuje, ale operace se z nějakého důvodu nezdaří, rename() zaručuje, že ponechá instanci newpath na místě.

Je tedy docela možné, že procesy spotřebovávající B v té době mohou během tohoto procesu vidět různé jeho verze (různé inody). Pokud se autoři pouze snaží zkopírovat stejný obsah a čtenáři obsah souboru jednoduše konzumují, může to být v pořádku, pokud dostanou různé inody pro soubory se stejným obsahem, budou stejně šťastní.

Druhý přístup využívající pevný odkaz vypadá lépe, ale vzpomínám si, že jsem dělal experimenty s pevnými odkazy v těsné smyčce na NFS od mnoha souběžných klientů a počítal úspěchy a stále se zdálo, že tam jsou nějaké závodní podmínky, kdy se zdálo, že dva klienti zadali operaci s pevným odkazem ve stejnou dobu, s stejný cíl, zdálo se, že oba uspěli. (Je možné, že toto chování souviselo s konkrétní implementací serveru NFS, YMMV.) V každém případě se pravděpodobně jedná o stejný druh race condition, kdy můžete skončit získáním dvou samostatných inodů pro stejný soubor v případech, kdy je souběžnost mezi autory ke spuštění těchto závodů. Pokud jsou vaši autoři konzistentní (oba kopírují A do B) a vaši čtenáři pouze konzumují obsah, mohlo by to stačit.

Nakonec jste zmínil zamykání. Bohužel zamykání vážně chybí, alespoň v NFSv3 (nejsem si jistý NFSv4, ale vsadil bych se, že ani to není dobré.) Pokud uvažujete o zamykání, měli byste se podívat na různé protokoly pro distribuované zamykání, možná mimo pásmo s skutečné kopie souborů, ale to je rušivé, složité a náchylné k problémům, jako jsou uváznutí, takže bych řekl, že je lepší se tomu vyhnout.

Pro více informací o atomicitě na NFS si možná budete chtít přečíst o formátu poštovní schránky Maildir, který byl vytvořen, aby se vyhnul zámkům a fungoval spolehlivě i na NFS. Dělá to tak, že všude uchovává jedinečné názvy souborů (takže na konci nedostanete ani poslední B.)

Možná je pro váš konkrétní případ poněkud zajímavější formát Maildir++ rozšiřuje Maildir o podporu kvóty poštovních schránek a činí tak atomickou aktualizací souboru s pevným názvem uvnitř poštovní schránky (takže by to mohlo být blíže vašemu B.) Myslím, že to Maildir++ zkouší připojit, což není na NFS ve skutečnosti bezpečné, ale existuje metoda přepočtu, která používá podobnou proceduru a je platná jako atomová náhrada.

Doufejme, že všechny tyto ukazatele budou užitečné!


Linux
  1. Jak rekurzivně kopírovat soubory podle přípony souboru?

  2. Jak udělat soubor řídký?

  3. Jak grep \nv souboru

  1. Jak přejmenovat soubor v Linuxu?

  2. Jak třídit soubor na místě

  3. Jak zkopíruji soubor s názvem začínající tečkou?

  1. Jak přesunout soubor v Linuxu

  2. Jak zkopírovat soubor do více adresářů v Linuxu

  3. Jak zkopírovat soubor pomocí Správce souborů Plesk