SafeStream: Atomické operace‎

Nette\Utils\SafeStream pro práci se soubory garantuje, že každá operace proběhne atomicky a izolovaně.

K čemu je to vlastně dobré? Začněme jednoduchým příkladem, který opakovaně zapisuje do souboru a následně z něj čte tentýž řetězec:

$s = str_repeat('Long String', 10000);

$counter = 1000;
while ($counter--) {
    file_put_contents('soubor', $s); // write it
    $readed = file_get_contents('soubor'); // read it
    if ($s !== $readed) { // check it
        echo 'řetězce se liší!';
    }
}

Může se zdát, že volání echo 'řetězce se liší!' nikdy nemůže nastat. Opak je pravdou. Schválně si zkuste tento skript spustit ve dvou oknech zároveň. Chyba se dostaví prakticky okamžitě.

Uvedený kód totiž není bezpečný, pokud se zároveň provádí vícekrát (tedy ve více vláknech). Což na internetu není nic neobvyklého, často se v tentýž okamžik připojuje více lidí k jednomu webu. Takže starat se o to, aby vaše aplikace fungovala spolehlivě i při provádění ve více vláknech (thread-safe), je velmi důležité. Jak totiž vidíte, nativní PHP funkce atomické nejsou. Jinak musíme počítat se ztrátou dat a vznikem těžko odhalitelných chyb.

Jak zajistit, aby se funkce file_put_contents nebo file_get_contents chovaly atomicky? Řešení nabízí bezpečný protokol SafeStream, pomocí něhož můžeme atomicky manipulovat se soubory prostřednictvím standardních PHP funkcí. Protokol se zpřístupní automaticky při načtení Nette Framework. A pak stačí jen uvést nette.safe:// před jménem souboru:

$handle = fopen('nette.safe://test.txt', 'x'); // před jméno souboru dáme nette.safe://

fwrite($handle, 'Nette Framework'); // zatím se píše do pomocného souboru

fclose($handle); // a teprve teď se přejmenuje na test.txt

Lze samozřejmě používat všechny známé funkce, například:

file_put_contents('nette.safe://test.txt', $content);

$ini = parse_ini_file('nette.safe://autoload.ini');

SafeStream vám garantuje:

  • atomicitu: soubor se zapíše buď celý, nebo vůbec
  • isolaci: nikdo nezačne číst soubor, který ještě není celý zapsán

Pokud zapisujete do existujícího souboru v režimu a (append), SafeStream vytvoří jeho kopii a teprve po úspěšném zápisu jí přejmenuje na původní název. Zapisovat v tomto režimu má tedy vyšší režii, než zápis v ostatních módech.