Nette\SafeStream a odolnost vůči pádu serveru

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
pmg
Člen | 372
+
0
-

Nette\SafeStream už prošel dlouhým vývojem. O jedné z dřívějších verzí (kdy ještě Safe nebyl Stream) David Grudl píše, že je odolná vůči pádu Apache.

Jak je na tom současná verze? Konkrétně: Co se stane, když spadne server při přepisu souboru?

Ideální by bylo dosáhnout stavu, kdy soubor bude buď kompletní, nebo žádný. Dovolil bych si zde teď nastínit řešení, které jsem použil pro ukládání kešovacích souborů.

  • Nenašel jsem reálnou situaci, kdy by bylo potřeba přidávat na konec souboru.
  • Pro vytvoření / přepsání souboru se použije techniky dočasného souboru. Jeho název se odvodí přímo od souboru cílového. Pokud se ho pak nepodaří otevřít v módu x, zjistí se, jak je starý. Pokud moc ne, znamená to, že soubor aktualizuje jiný proces, není potřeba dělat nic. V opačném případě (tzn. poslední úprava byla přerušena) se smaže.
  • Před přejmenováním dočasného souboru je nutné odstranit starý. Použije se proto malý trik. Místo mazání starého souboru se mu jen přidá přípona .bak a smaže se až po přejmenování dočasného souboru na originál.
  • Čtení realizujeme pomocí @file_get_contents s tím, že v případě neúspěchu zkusíme i variantu s .bak.

Jaké to má výhody? Při běžném čtení jsem naměřil o třetinu kratší čas, update zhruba dvakrát pomalejší, ale hlavně – data by měla zůstat konzistentní.

Já vím, SafeStream je koncipován obecně a mnou navržené řešení je příliš aplikované. Dále, stream lze také použít s parse_ini_file. To by se sice dalo řešit pomocí nějakého protokolu inline:// nebo dočasných souborů; ale není to ono. Myslel jsem spíš, že by v Nette mohla přibýt třída pro práci s keší, ukládání konfigurace ap.

Je to potřeba? Zajímá mě, co na to ostatní.

David Grudl
Nette Core | 8218
+
0
-

Zápis do dočasného souboru s přejmenováním dělá SafeStream tehdy, pokud zapisujete soubor v režimu x, x+, nebo v režimu a a+ w w+ a soubor ještě neexistuje. V takovém případě je skutečně garantováno, že soubor se buď zapíše celý, nebo vůbec.

SafeStream navíc kontroluje, zda se data zapsala na disk všechna (tj. jestli není disk plný).

pmg
Člen | 372
+
0
-

To všechno je v pořádku. Problém ale nastane, když server vypadne během přepisu. Pro zajištění konzistence bych buď musel starý soubor na chvíli smazat (a riskovat, že se např. bude generovat vícekrát), nebo to řešit na nižší úrovni pomocí nějakých kontrolních dat.

Edit: Vidím, že Nette v současné verzi kešování přímo podporuje: při použití serializace by to mělo být proti nekompletním souborům rezistentní. Díky za to!

Edit: Bug Report: Jak jsem tak koukal na soubor FileStorage.php, na řádku 228 by asi mělo být @unlink($metaFile); nehází to chybu, ale $file není definovaná

Editoval pmg (23. 5. 2008 21:47)

David Grudl
Nette Core | 8218
+
0
-

Ona celá věc je ještě složitější.

Že by vypadl server je extrémě nepravděpodobné. Daleko pravděpodobnější je, že během zápisu dojde k chybě v PHP skriptu nebo jeho předčasnému ukončení. Nicméně i v takovém případě se soubor korektně uzavře a těžko lze určit, jestli byl nebo nebyl zapsán celý.

Takže ověřování konzistence bych spíš nechal na vyšší vrstvě.

ad dočasné soubory a SafeStream: díval jsem se, že dočasný soubor se pod Unixem přejmenuje na cílový ihned po získání zámku. Účelem tedy je atomické provedení operací „vytvořit soubor + získat zámek“, což jinak udělat nelze.

pmg
Člen | 372
+
0
-

Díky za info.

Tou vyšší vrstvou může být třeba právě Cache v Nette. Souhlasím, že SafeStream se pro takové ověřování nehodí. Poslední verze bude splňovat všechny předpoklady najednou: nebude potřeba vytvářet víc souborů, zaručí se atomicita operací a konzistence dat a soubory půjde vkládat přímo přes include. Co víc si přát?

Ad dočasné soubory: Unix se v tomto směru vůbec chová přívětivě: není např. nutné skript uspávat, aby mohl smazat / přejmenovat soubor.