Operační systémy
Obsah Soubory
markdown

Zurnalovani-a-konzistence.md

11 kB 168 řádků Změněno Zobrazit na GitHubu Stáhnout
markdown
# Žurnálování a co přežije výpadek proudu Přesuneš soubor do jiného adresáře. Na disku to znamená dvě změny: přidat záznam sem a odebrat ho tam. Disk umí zapsat jeden sektor atomicky - buď celý, nebo vůbec. Dva sektory naráz nezaručí nikdo. Vytáhni přesně v tu chvíli šňůru ze zásuvky. Soubor je teď buď na dvou místech naráz, nebo na žádném. Po zapnutí musí souborový systém poznat, co se stalo, a spravit to - jen z toho, co na disku zbylo. Tahle stránka je o tom, jak se to dělá a co ti to zaručí. Předpokládá [souborový systém](Souborovy-system), tedy i-uzel, adresář a alokaci bloků. Neřeší zálohy ani RAID, protože ani jedno není odpověď na tuhle otázku. ## Než přišel žurnál, běžel `fsck` přes celý disk Starý postup byl jednoduchý a brutální. Po nečistém vypnutí prošel `fsck` celý svazek a hledal nesrovnalosti: blok přidělený dvěma souborům, i-uzel bez odkazu, počet odkazů, který nesedí. Trvalo to úměrně počtu i-uzlů, ne počtu změn: u terabajtového disku desítky minut, u plného a pomalého klidně hodiny. A výsledek nebyl jistý - co `fsck` nezařadil, skončilo v `lost+found` pod číslem i-uzlu. Server, který po výpadku nabíhá hodinu a pak nabídne dvě stě souborů bez jmen, je celý důvod, proč žurnál vznikl. ## Zápis napřed, provedení potom Žurnál je vyhrazená oblast na témž svazku, do které se **nejdřív zapíše, co se chystá**, a teprve potom se to provede. Anglicky *write-ahead logging*, stejný nápad jako v databázích. ### Jak žurnál funguje ```mermaidsequenceDiagram    participant J as Jádro    participant Z as Žurnál    participant D as Místa na disku    J->>Z: 1. zápis záměru (oba změněné adresáře)    J->>Z: 2. potvrzovací záznam    Note over Z: tady se změna stává nezvratnou    J->>D: 3. provedení změn na jejich místa    J->>Z: 4. uvolnění záznamu ze žurnálu``` Krok 2 je celý trik. Potvrzovací záznam je malý a zapíše se jedním zápisem, takže na disku buď je, nebo není. Tím se z velké skupiny změn stane operace se dvěma možnými výsledky. Po výpadku se žurnál při připojení jen přečte. Potvrzené transakce se provedou znovu, nepotvrzené se zahodí. Trvá to vteřiny, protože se čtou desítky až stovky megabajtů, a ne celý disk. ### Tři režimy ext4 a rozdíl mezi nimi Ext4 umí tři režimy a liší se v jediné věci: co všechno patří do transakce. | Režim | Co jde do žurnálu | Rychlost | Co se ti může stát ||---|---|---|---|| `data=journal` | metadata i obsah | nejpomalejší, data se píší dvakrát | nejmíň, obsah sedí s posledním potvrzením || **`data=ordered`** | metadata, data se zapíšou dřív | výchozí kompromis | soubor bude zkrácený nebo prázdný || `data=writeback` | metadata, na pořadí se nehledí | nejrychlejší, rozdíl bývá malý | v souboru najdeš zbytky cizího smazaného souboru | Rozdíl mezi `ordered` a `writeback` je v pořadí, ne v obsahu žurnálu. V `ordered` jsou data na místě **dřív**, než se potvrdí metadata, takže i-uzel nikdy neukáže na blok, do kterého se nezapsalo. **Doporučení: nech `ordered`.** `data=journal` má smysl jen tam, kde je zápis vzácný a cena chyby vysoká. Režim se nedá změnit za běhu - `mount -o remount,data=writeback` skončí hláškou `Cannot change data mode on remount`. ### Copy-on-write jako druhá cesta Btrfs a ZFS řeší tentýž problém opačně: **nikdy nepřepisují na místě**. Nový obsah jde do volných bloků, přepíšou se ukazatele nad ním a nakonec se jedním atomickým zápisem přehodí kořen stromu. Do té chvíle je na disku celá stará verze. **Snímky (snapshots)** z toho plynou zadarmo: jsou to podržené staré kořeny, takže vzniknou okamžitě a nezaberou nic. **Kontrolní součty** dávají smysl, protože se blok stejně zapisuje celý znovu. Cena je fragmentace: databáze a virtuální disky přepisují bloky uvnitř velkého souboru a na CoW z nich vzniknou desítky tisíc kousků. Proto se u nich CoW vypíná: ```bashmkdir /var/lib/mysql-nocowchattr +C /var/lib/mysql-nocow   # atribut se dědí, soubory uvnitř vzniknou bez CoW``` **Doporučení: btrfs na kořen a domovy, kde snímky před aktualizací opravdu použiješ.** Na svazek s databází nebo obrazy virtuálních strojů sáhni po ext4 nebo XFS. ## Žurnál chrání strukturu, ne tvoje data Tohle je nejdůležitější věc na celé stránce: **žurnál chrání konzistenci souborového systému, ne obsah tvých souborů.** Zaručuje ti, že každý blok patří nejvýš jednomu souboru, každý i-uzel má správný počet odkazů a bitmapa volných bloků odpovídá skutečnosti. To je všechno. Soubor o nula bajtech je podle těchhle pravidel v pořádku. Systém nastartuje, `fsck` nic nenajde - a tvoje konfigurace je prázdná. Konzistentní neznamená správný. ## Kde končí odpovědnost systému ### fsync a page cache Když program zavolá `write`, data skončí v [page cache](Vstup-a-vystup) a volání se vrátí. Na disku nejsou. Jádro je tam vyplaví během vteřin až desítek vteřin. Jediné, co to změní, je [systémové volání](Systemova-volani) `fsync`. Vrátí řízení, teprve až je obsah na trvalém úložišti, a stojí to - na NVMe stovky mikrosekund, na plotnovém disku jednotky až desítky milisekund. Bezpečná výměna souboru má proto čtyři kroky a žádný se nedá vynechat: ```cfd = open("app.conf.tmp", O_WRONLY|O_CREAT|O_TRUNC, 0600);write(fd, data, len);                  // 1. píšeš stranou, původní soubor zůstává celýfsync(fd);                             // 2. obsah je na disku dřív, než na něj ukáže jménoclose(fd);rename("app.conf.tmp", "app.conf");    // 3. výměna jména, atomická z definicedirfd = open(".", O_RDONLY|O_DIRECTORY);fsync(dirfd);                          // 4. i přejmenování jsou metadata a musí se potvrdit``` Krok 2 bez kroku 3 ti nechá dva soubory. Krok 3 bez kroku 2 ti nechá jméno ukazující na prázdno. Krok 4 vynechá skoro každý, protože bez něj to zdánlivě funguje. ### Spor o ext4 a zpožděnou alokaci, rok 2009 Ext4 zavedl zpožděnou alokaci: bloky se nepřidělí při zápisu, ale až při vyplavení, což dovolí lepší rozvržení na disku. Okno mezi zápisem a přidělením se tím prodloužilo na desítky vteřin. Desktopové aplikace tehdy ukládaly nastavení tak, že zapsaly dočasný soubor a hned ho přejmenovaly - bez `fsync`. Na ext3 to díky pořadí zápisů většinou prošlo. Na ext4 po pádu ovladače grafiky, který tehdy nebyl vzácný, zůstávaly lidem konfigurační soubory o nulové délce. Autoři aplikací tvrdili, že jim to rozbil souborový systém. Ted Ts'o, správce ext4, tvrdil, že ty aplikace byly rozbité vždycky a ext3 jim to jen zakrýval. Obojí byla pravda. Do jádra 2.6.30 přibylo automatické vyplavení při přejmenování přes existující soubor (volba `auto_da_alloc`). **Poučení: chování, na které se spoléháš, ale které ti nikdo neslíbil, není záruka - je to jen aktuální implementace.** ## Bariéry a hardware, který lže Celá konstrukce stojí na předpokladu, že potvrzený zápis se opravdu stal. Disky mají vlastní vyrovnávací paměť a bez donucení potvrzují dřív, než data uloží. Proto jádro při potvrzení transakce posílá příkaz k vyprázdnění cache. Ext4 to dělá ve výchozím stavu, ext3 dlouho ne, a odtud se dodneška vrací rada vypnout to kvůli výkonu. **Volbu `nobarrier` nasazuj jen na řadič se zálohovanou pamětí.** Jinak si za jednotky procent kupuješ přesně tu poruchu, proti které žurnál stojí. Levná SSD bez ochrany proti výpadku napájení jsou horší případ. Nemají dost energie, aby po ztrátě proudu dopsala vnitřní cache, a testy spotřebních modelů opakovaně ukázaly ztrátu dat i tam, kde systém udělal všechno správně. **Tohle je jediná situace, ve které ti žurnál nepomůže** - pracuje s potvrzeními, která nejsou pravdivá. ## Kde žurnál nepomůže vůbec **Proti tiché korupci dat je slepý.** Když se bit na disku převrátí sám, struktura zůstane v pořádku a není co přehrávat. Poznají to jen kontrolní součty dat, které mají ZFS a btrfs. Ext4 kontroluje součty metadat, obsah nikoli - vrátí ti poškozená data bez varování. **Proti `rm -rf` je slepý taky.** GitLab v roce 2017 takhle přišel o šest hodin produkčních dat a zachránit ho nemělo co: z pěti způsobů zálohy nefungoval ani jeden. Na obojí je záloha na jiném stroji, ne volba souborového systému. ## Diagnostika ```bashdumpe2fs -h /dev/nvme0n1p2      # hlavička superbloku: hledej has_journal a velikost žurnálutune2fs -l /dev/nvme0n1p2       # totéž plus počty připojení a interval kontrolygrep ' / ' /proc/mounts         # s jakými volbami je kořen připojenýdmesg | grep -i 'EXT4-fs'       # režim při připojení i chyby``` Režim poznáš z hlášky při připojení, která končí slovy `with ordered data mode`. Když ve výpisu voleb žádné `data=` není, platí výchozí `ordered`. `fsck` nikdy nepouštěj na připojený svazek: čte struktury, které se pod ním mění, takže „opraví“ stav, který mezitím neplatí. Kořen odpojit nejde, a tak se kontrola vynucuje při [startu](Start-systemu) parametrem jádra `fsck.mode=force` - ne souborem `/forcefsck`, který návody dodneška opisují. O volbách připojení je [VFS a montování](VFS-a-montovani). | Příznak | Kde je problém ||---|---|| `recovering journal` v `dmesg` po startu | normální přehrání po nečistém odpojení || `EXT4-fs error (device nvme0n1p2)` | poškozená metadata, svazek přejde do jen pro čtení || Konfigurační soubor má po pádu nula bajtů | aplikace přejmenovala bez `fsync` || V souboru je binární smetí z cizího souboru | svazek běží v `data=writeback` || Chybí i to, co bylo zapsané minuty předtím | disk potvrzuje z cache, nebo je vypnuté vyprazdňování || `has_journal` chybí ve výpisu `dumpe2fs` | ext2, nebo ext4 založený bez žurnálu || Data jsou poškozená, ale `fsck` je čistý | tichá korupce, ext4 ji u dat nepozná | ## Co si odnést **Žurnál chrání strukturu, ne obsah.** Čistý `fsck` neznamená, že tvůj soubor má data. **Nech `data=ordered`.** `journal` je pomalý, `writeback` pouští do souborů cizí zbytky. **Bez `fsync` nemáš slíbeno nic.** `write` skončí v paměti, ne na disku. **Bezpečná výměna má čtyři kroky.** Zápis, `fsync`, `rename`, `fsync` adresáře. **Copy-on-write přidá snímky a součty, ale i fragmentaci.** Jiná odpověď na tutéž otázku. **Lhoucí hardware porazí každý žurnál.** Nepravdivé potvrzení se softwarem obejít nedá. **Proti smazání a bitrotu pomáhá záloha.** Souborový systém na to není a nikdy nebyl. ## Kam dál - **[Souborový systém](Souborovy-system)** - i-uzly a adresáře, tedy struktura, kterou žurnál drží pohromadě- **[Vstup a výstup](Vstup-a-vystup)** - page cache a plánování zápisů, odkud se `fsync` bere- **[Start systému](Start-systemu)** - co se s nečistě odpojeným svazkem děje při bootu- **[Nástroje a diagnostika](Nastroje-a-diagnostika)** - `dmesg`, `/proc` a zbytek výbavy na hledání příčiny