PHP: Výsledky srovnání funkcí file_(get)put_contents & f(read/write/flush)

Kit

  • *****
  • 661
    • Zobrazit profil
    • E-mail
Správně se to dělá tak, že se proces nejprve pokusí zamknout všechny soubory, které bude potřebovat ke své práci. Pokud některý ze zámků selže, všechny odemkne, počká náhodnou dobu a pokusí se znovu nasadit všechny zámky.


Myslím, že flock není atomický - https://www.reddit.com/r/PHP/comments/9ec4tz/a_locking_file_cache_in_php/e6cqg19/
Mohl byste to rozepsat? Atomicita znamená, že se daná funkce provede celá nebo vůbec a že nikdo nemůže vidět nějaký vnitřní mezistav té funkce. V případě zámků atomicita znamená, že na souboru nějaký zámek je nebo není – nemůže nastat stav, že by tam zámek byl tak trochu. Atomicita se ale samozřejmě posuzuje z hlediska jedné funkce – to, že se něco může stát před voláním flock() nemá na atomicitu funkce flock() vliv.
Myslel jsem to tak, jak se píše v tom odkazu, tzn. že mezi fopen a flock se může dostat jiný proces. Spolehlivější mně připadá zapisování s file_put_contents, který může mít parametr LOCK_EX. (To samozřejmě v praxi neřeší problém čtení, kde mezi čtením a zápisem může druhý PHP proces zapsat do čteného souboru - tím dojde ke ztrátě dat z tohoto druhé PHP procesu, protože daný soubor v zápětí přepíše ten první PHP proces. Tohle si musí programátor ošetřit ručně.)

Pokud bych to tedy měl říci přesně, tak flock() je atomická funkce, ale ke svému využití potřebuje fopen(), a spojení fopen()+flock() atomické není.


Myslím, že flock není atomický - https://www.reddit.com/r/PHP/comments/9ec4tz/a_locking_file_cache_in_php/e6cqg19/
Začíná se mi pomalu vybavovat, proč jsem se tenkrát rozhodl využít pro zamykání mkdir ;)
V podstatě tam píší jen to, že mezi voláním fopen() a flock() si ten soubor může zamknout někdo jiný. Tedy nic, co by se nedalo ošetřit testem návratové hodnoty flock().
Zamykání přes mkdir() má nevýhodu v tom, že když ten proces spadne, kdo to po něm uklidí?
Ano uklízení po mkdir() musíš ošetřit ručně. Nevím jak je to přesně u flock(), ale předpokládám, že i tam se ti zámek může seknout a pak musíš mít nějakou vlastní funkci, která ji natvrdo odblokuje.

Nevýhodu vidím v tom, že testem návratové hodnoty flock() vytvoříš smyčku mezi flock() a fopen(). Pokud bys pustil vytěžující scripty (o což se Exkalibr snaží), tak by se smyčka nemusela stihnout dokončit do konce běhu PHP scriptu a script by spadl. Pokud si uděláš vlastní funkcí pomocí mkdir(), tak ji zavoláš ještě před fopen().

Teď jsem opakoval T7 jen s 6 cyklama a jen ze dvou prohlížečů a přesto došlo ke 4 kolizím. Oproti tomu T8 s 10 cyklama ze čtyř firefoxů udělal jen 1 chybu ... za bez použití file_exists. Ale taky se dívám že výsledná doba čekání u toho selhání byla poměrně malá 0.864s.
Tohle testování je k ničemu. Pokud nemáš správně zámky, tak pořád testuješ nezamknuté soubory a vůbec není podstatné kolik je tam chyb. Pokud paralelní php scripty pustíš přesně ve stejný okamžik, tak tam těch chyb bude teoreticky více než když je spustíš s malým časovým odstupem - časový odstup u takového testu sníží riziko překřížení mezi jednotlivými PHP procesy.

V testech je naopak výhodné, pokud se ty procesy co nejvíc potlučou mezi sebou - proto jsou ty časové odstupy zbytečné a kontraproduktivní.
Ano souhlasím, jen jsem narážel na to, že tady Exkalibr pořád háže nějaké čísla s počtem chyb, které mají jen tu vypovídací hodnotu, že to má špatně zamknuté. Jestli je tam 1 chyba nebo 30 chyb, tak je to jedno.

Myslel jsem to tak, jak se píše v tom odkazu, tzn. že mezi fopen a flock se může dostat jiný proces.
To ale neznamená, že flock() není atomický.

Pokud bych to tedy měl říci přesně, tak flock() je atomická funkce, ale ke svému využití potřebuje fopen(), a spojení fopen()+flock() atomické není.
To, že volání dvou funkcí po sobě není atomické, je naprostý základ. Bez pochopení téhle věci je zbytečné pokoušet se programovat cokoli se zámky nebo řešit nějakou atomicitu.


Kit

  • *****
  • 661
    • Zobrazit profil
    • E-mail
Myslím, že flock není atomický - https://www.reddit.com/r/PHP/comments/9ec4tz/a_locking_file_cache_in_php/e6cqg19/
Začíná se mi pomalu vybavovat, proč jsem se tenkrát rozhodl využít pro zamykání mkdir ;)
V podstatě tam píší jen to, že mezi voláním fopen() a flock() si ten soubor může zamknout někdo jiný. Tedy nic, co by se nedalo ošetřit testem návratové hodnoty flock().
Zamykání přes mkdir() má nevýhodu v tom, že když ten proces spadne, kdo to po něm uklidí?
Ano uklízení po mkdir() musíš ošetřit ručně. Nevím jak je to přesně u flock(), ale předpokládám, že i tam se ti zámek může seknout a pak musíš mít nějakou vlastní funkci, která ji natvrdo odblokuje.

Nevýhodu vidím v tom, že testem návratové hodnoty flock() vytvoříš smyčku mezi flock() a fopen(). Pokud bys pustil vytěžující scripty (o což se Exkalibr snaží), tak by se smyčka nemusela stihnout dokončit do konce běhu PHP scriptu a script by spadl. Pokud si uděláš vlastní funkcí pomocí mkdir(), tak ji zavoláš ještě před fopen().

Proto se do toho zamykacího cyklu vkládá náhodná prodleva, aby se snížilo riziko náhodné kolize.

Flock je automaticky odstraněn zavřením souboru. Není třeba řešit jeho odstranění ručně.

Pokud skript spadne, tak spadne. Obvykle to ničemu nevadí a je to zcela běžné. Klient si ho spustí znovu.

Teď jsem opakoval T7 jen s 6 cyklama a jen ze dvou prohlížečů a přesto došlo ke 4 kolizím. Oproti tomu T8 s 10 cyklama ze čtyř firefoxů udělal jen 1 chybu ... za bez použití file_exists. Ale taky se dívám že výsledná doba čekání u toho selhání byla poměrně malá 0.864s.
Tohle testování je k ničemu. Pokud nemáš správně zámky, tak pořád testuješ nezamknuté soubory a vůbec není podstatné kolik je tam chyb. Pokud paralelní php scripty pustíš přesně ve stejný okamžik, tak tam těch chyb bude teoreticky více než když je spustíš s malým časovým odstupem - časový odstup u takového testu sníží riziko překřížení mezi jednotlivými PHP procesy.

Ale ty zámky jsou přece nastaveny správně, copak si neviděl kód?

Pokud skript spadne, tak spadne. Obvykle to ničemu nevadí a je to zcela běžné. Klient si ho spustí znovu.

Pokud spadne tak tam zůstane ten vytvořený adresář, který nikdo nesmazal. Ten script pak nepůjde provést. Musel bych tam spouštět čas od času udržovací script, který by promazal ty staré adresáře - ale to je zase zátěž na víc a navíc to neřeší problém úplně. Protože nějakou dobu to spustit nepůjde.

Správně se to dělá tak, že se proces nejprve pokusí zamknout všechny soubory, které bude potřebovat ke své práci. Pokud některý ze zámků selže, všechny odemkne, počká náhodnou dobu a pokusí se znovu nasadit všechny zámky.

Já bych ten soubor mohl mít jen jeden, ale pokud použiju rejstříky a indexy - další dva soubory pro urychlení parsování/hledání - tak to budou 3.

Co když ten skript spadne, soubor zůstane zamknut? Jak dlouho?

Kit

  • *****
  • 661
    • Zobrazit profil
    • E-mail
Pokud skript spadne, tak spadne. Obvykle to ničemu nevadí a je to zcela běžné. Klient si ho spustí znovu.

Pokud spadne tak tam zůstane ten vytvořený adresář, který nikdo nesmazal. Ten script pak nepůjde provést. Musel bych tam spouštět čas od času udržovací script, který by promazal ty staré adresáře - ale to je zase zátěž na víc a navíc to neřeší problém úplně. Protože nějakou dobu to spustit nepůjde.

Nepoužívám mkdir(), ale flock(), který je spolehlivý a při zavření souboru (i při pádu procesu) se sám odemkne.

Kit

  • *****
  • 661
    • Zobrazit profil
    • E-mail
Správně se to dělá tak, že se proces nejprve pokusí zamknout všechny soubory, které bude potřebovat ke své práci. Pokud některý ze zámků selže, všechny odemkne, počká náhodnou dobu a pokusí se znovu nasadit všechny zámky.
Já bych ten soubor mohl mít jen jeden, ale pokud použiju rejstříky a indexy - další dva soubory pro urychlení parsování/hledání - tak to budou 3.

Tak si zamkni jen jeden a říkej mu semafor.

Rejstříky, indexy,... přiznej se, že ty v PHP vyrábíš databázi?

T8 mkdir, jinak

Kit, děláš mi to rozhodování čím dál těžší. Flock sice spolehlivý je, ale nezaručuje že mezi otevřením a flockem se k souboru, nedostane jiný proces.  :o

Teď jsem opakoval T7 jen s 6 cyklama a jen ze dvou prohlížečů a přesto došlo ke 4 kolizím. Oproti tomu T8 s 10 cyklama ze čtyř firefoxů udělal jen 1 chybu ... za bez použití file_exists. Ale taky se dívám že výsledná doba čekání u toho selhání byla poměrně malá 0.864s.
Tohle testování je k ničemu. Pokud nemáš správně zámky, tak pořád testuješ nezamknuté soubory a vůbec není podstatné kolik je tam chyb. Pokud paralelní php scripty pustíš přesně ve stejný okamžik, tak tam těch chyb bude teoreticky více než když je spustíš s malým časovým odstupem - časový odstup u takového testu sníží riziko překřížení mezi jednotlivými PHP procesy.

Ale ty zámky jsou přece nastaveny správně, copak si neviděl kód?
Viděl, ale je to pro mě nepřehledné a nechce se mi v tom vrtat, ale jen tak namátkou. V tom T7 co máš na stackoverflow.com vidím zhruba toto:

//read
fopen $sname
flock lock_sh
$s=fread
flock lock_un
fclose $sname
touch $sname

//write
fopen $tname
flock lock_ex
fwrite $s
fflush
flock lock_un
fclose

Když ti více PHP procesů zároveň provede řádek "fopen $tname" a pak jeden proces přejde na řádek flock lock_ex, tak neskončí ti náhodou zbývající procesy hláškou "Couldn't get the lock!"?

... pak mám dojem, že příkaz "touch("$tname",$fsize,$p);" používáš ve chvíli kdy jiný proces může mít daný $tname soubor zamklý pomocí lock_ex.