Ki a hülye, én vagy te?

Kérdeztem egy ilyet:
"Now, that nginx supports running threads, are there plans to convert at least DAV PUTs into it's own thread(pool), so make it possible to do non-blocking (from nginx's event loop PoV) fsync on the uploaded file?"
http://mailman.nginx.org/pipermail/nginx/2018-February/055719.html

A háttere ennek az, hogy az nginx nem csinál fsync-et a PUT után, így nem tudhatod, hogy a művelet tényleg sikeres volt-e, vagy csak az OS memóriájában kallódik még a fájl.
Erre lenne megoldás, hogy vagy az egész PUT-ot egy külön threadben intézik, vagy legalább a végén egy fsyncet tolnának egy threadpoolból, aminek a fő threadből (async) megvárják az eredményét, és utána adják vissza az OK-t.

És erre ilyen válaszok jöttek, egy részük @nginx-es e-mail címel:
"(Also, trying to do fsync() might not be the best idea even in threads. A reliable server might be a better option.)"
"I understand why one may need consistency, but doing so with fsyncing is non-sense."

"So it’s not a great way to ensure consistency.

But there are cheaper ways to achieve the same consistency and reliability characteristics:

If you are using Linux then your reads and write swill occur through the page cache - so the actual disk itself really doesn’t matter (whilst your host is up).
If you want to protect against loss of physical disk then use RAID.
If you want to protect against a random power failure then use drives with battery backed caches, so writes will get persisted when a server restarts after a power failure
If you want to protect against a crazy person hitting your server with an axe then write to two servers ...
But the bottom line is separation of concerns. Nginx should not use fsync because it isn’t nginx's business."

"The question here is - why you want the file to be on disk, and not just in a buffer? Because you expect the server to die in a few seconds without flushing the file to disk? How probable it is, compared to the probability of the disk to die? A more reliable server can make this probability negligible, hence the suggestion."

Ezek elvileg komoly, értelmes emberek. Elbizonytalanodtam. Csak én megyek szembe az autópályán?

Hozzászólások

Nem értelmetlen emberek, csak nagyon benne vannak a saját problem domainjükben.
Írd meg a fileba írás részt goban amúgy és ezt a részt proxyzd oda.

Pont ez jutott eszembe nekem is, de nem tudtam ilyen szépen megfogalmazni. :)
Nem akartam nagyon kifejezni a véleményem, de ez annyira tömör és kifejező, hogy erősen gondolkozom, hogy megírom nekik.

Megoldom, csak nyilván egyszerűbb és szebb lenne, ha nem kellene emiatt hackeket csinálni.
Illetve ha lesz időm, megnézem majd, mennyire nehéz threadbe tenni ezeket, akkor az lenne a legtisztább, bár ezek után kétlem, hogy befogadnák a patchet, még ha opcionálisan kapcsolható lenne is...

Itt azt írják, hogy a threadből fsyncelős trükk nem biztos, hogy sokat ér, mert ugyanúgy blokkolja mindenhol a writeokat. Mások azt írják, filesystem függő a dolog. Nem egy mai írás, meg kellene vizsgálni. Adott esetben azt is jelentheti, hogy egy fsync() sima belegányolását is érdemes lenne tesztelni.

Másik lehetőség a file O_SYNC megnyitása, egy tesztet megér. (És új fájlnál, a parent directoryra is nyomni egy fsyncet...)

Amúgy az nginx jellemzően nem AIO-t használ linuxon? Van aio_fsync is, nem?

Másról van ott szó:
"But I started to have the feeling that this would be totally useless, as the write(2) call would block anyway if there was a slow fsync() going on against the same file"
Az én esetemben a külön threadben futó fsync-ek mindegyike más fájlt érint.

A sync írás azért rossz, mert a fő nginx szálat blokkolja, ez nagyon durva késleltetéseket visz bele a működésbe.

Van aio_fsync (Linuxon, én pedig cross platform megoldást javasoltam), de:
"Note that this is a request only; it does not wait for I/O completion."
szóval itt megint nem játszik.

Ah, bocs, félreértelmeztem a mondat értelmét. Nyilván, az asyncnak ez a lényege.
Valamiért azt szűrtem le, hogy ez egy önálló, mindentől független függvény, azaz fire&forget, és a művelet eredményét nem tudod sehogy sem visszakapni, de most, hogy még fél percet rászántam látom, hogy van aio_return is, amivel ellenőrizhető az eredmény. :)
Szóval igen, ez akár jó is lehetne, köszi!
(nem ismertem, nincs is vele tapasztalatom a konkrét implementációkat tekintve, ahogy az látszik :)

Lehet hülye kérdés: a PUT után akkor az se garantált így, hogy azonnal el tudom érni a fájlt? Vagy hogy a szerver oldal bármelyik része el tudja érni a fájlt a PUT "elvileg" sikere esetén? A lemez eléréskor a buffer is lekérdezésre kerül? És ha az fsync mégse sikeres, hogy jelzi vissza a PUT kérőjének?

(Kicsit úgy érzem magam, mint amikor a js async, non-blocking beszélgetésekben felvetem, hogy nem úgy kellett volna indulni, hogy lehessen blocking programot építeni, és amit tudok kiszervezem külön szálakra? Nem az lenne a normális, hogy alapból van blocking és non-blocking, és nem pedig 10+ év fejlődés eredménye a promise és miegymás után a synchronous végrehajtás? De tényleg lehet valamit nem értek csak.)

Ha nem blocking IO-ban, meg synchronous programming-ban gondolkosz, hanem mondjuk Reactive Programming-ban es/vagy Event-Loop-ban akkor ezeknek a dolgonak nagyon is van ertelme.

Ha tenyleg erdekel a tema:
https://doc.akka.io/docs/akka/current/general/terminology.html#terminol…
https://gist.github.com/staltz/868e7e9bc2a7b8c1f754

Hmmm azért a felsorolt érvek a RAIDről, meg a battery backed cache-ről kicsit fogalmatlannak tűnik :| Ha fáj nekik performance miatt, akkor tegyék opcionálisra ... szerintem van értelme annak amit írtál.

Pont ez ütötte ki nálam is a biztosítékot. Olyan, mintha egy idegen fajjal próbálnék kapcsolatot teremteni, aminek az űrhajója éppen most lépett ki a hipertérből, de képtelen lennék nekik elmagyarázni, hogy 2+2=4.
És még ha csak random, pálya széléről bekiabáló "majd én felvilágosítom az idiótát" sysadmin pistikék lennének, de nem, összezárt csatárláncban tolja mindegyik fullba a kretént. :)

Egyébként az a vicc, hogy a mostani implementáció kb. a legszarabb teljesítmény szempontból.
Lazán előfordulhat, hogy egy fájl syscall hosszú ms-okig tart, amíg az egész programjuk áll, és nem csinál semmit. És egy PUT-nál is történik azért pár ilyenből.
Nem véletlen, hogy elkezdtek threades irányba nézelődni.

"a végén egy fsyncet tolnának egy threadpoolból"

Ez hogyan skalazodna szerinted?

Egyebkent nem egeszen vilagos szamomra, mit szeretnel elerni, vagy talan inkabb az, hogy miert. Mi a cel / use case? Mekkora fajlokrol van szo? Egyszerre hany kliens hasznalna ezt a feature-t?

Nem értem a kérdésed.
Nyilván lesz annyi threaded, amennyi fsync-ed épp fut. Hogyan skálázódna? Minek kell itt skálázódnia?
Ha simán indítasz mindegyik fsyncnek egy threadet, a modern OS-eken már ezzel sem lesz semmi problémád, de ezen lehet még optimalizálni, ha annyira szükségesnek találja valaki.
Nyilván ha lassan bírnak csak kimenni a fájlok a diszkre, sok thread lesz.

A use case az, hogy fájlokat pakolgatunk (web)szerverekre, és szeretném tudni, hogy ha visszajött az OK, akkor az a fájl ott van.
Változó méretű fájlokról, párszáz bájtostól pár MB-osig.
Sok kliens (de bőven nem kezelhetetlen méretű), de a probléma szempontjából nem érzem relevánsnak.

Ha esetleg arra célzol, hogy az elvi problémának van jobb megoldása is (pld egyetlen "spool" fájlba írni a bejövő adatokat, és csak azt fsync-elni, összevárva sűrűbb időben több requestet, és egyebek), azt tudom. De az már nem egy standard HTTP szerver, standard fájlokkal.

Gondolom a cél az, hogy lerövidítse az időablakot a PUT és az automatikus OS által kért sync között azzal, hogy már a PUT pillanatában lefut a sync, és így az esély hogy a hardver akkor döglik meg mikor a fájl a memóriában van, ennyivel kisebb.

Felmerül részemről a kérdés hogy a sync intervallumok sűrűbbre állítása nem jobb teljes megoldás-e?

Amúgy olyan szolgáltatása nincs az OS-nek, hogy megmondja: ez a fájl írás most már diszken van. Valami finished event listener callback-szerű? (Komolyan kérdezem, nem ismerem ilyen mélységben ezt a területet.)

Vagy esetleg időbélyeg alapján: az x bélyeg előtti műveletek le vannak tárolva.

Az fsync-nek az a baja, hogy ha túl sokszor kikényszerítjük, akkor valóban szuboptimális lehet a teljes sávszélesség.

Nem tudok róla. Anno javasoltam valami hasonlót (a normál fsync interfésszel, így az alkalmazásokat sem kellene módosítani, illetve az OS-t sem, mert ez a FS-ben lenne implementálva). Ott is azt érzem, hogy nem teljesen értettek meg. :)
https://illumos.topicbox.com/groups/zfs/T5e1c98acd415ea96-M0daf2d114ef0…

Egy user program ne mondja mar meg mikor irodjon ki a page cache, a page cache kezelese a kernel dolga, ezt kikenyszeriteni userspacebol azon kivul, hogy lassu, altalaban felesleges is. A valaszok amiket kaptal teljesen revelansak. Ha csokkenteni akarod a kiirasi idot akkor ezt nezegesd.

¨Nginx should not use fsync because it isn’t nginx's business¨ Ez a valasz mindent elmond.

Szerintem ott van az értetlenség gyökere, hogy két fő kérdés van: egyrészt ők azt látják, hogy nginx szempontból mindegy, hogy a disk-ken van, vagy a memóriában, a program úgy látja, hogy megtörtént az irás, nincs konzisztencia probléma, az adat helye legyen absztrakt, ez megoldott, az adatbiztonsági kérdésben (balta, villámcsapás, áramkimaradás) pedig úgy gondolják, hogy hát egy jó szerveren / szolgáltatáson nem történik ilyen okból adatvesztés, ez már megoldott.

Tényleg mennyivel hatékonyabb a hálózati pufferből apránként kapott adatokat pár kB-onként flusholni a diszkre, ahelyett, hogy a végén hívnál egy fsyncet az egészre!
Valóban így kellene csinálni mindenhol.
Bár némi ellentmondást érzek ezzel is, mert ugye ezt írtad:
"Egy user program ne mondja mar meg mikor irodjon ki a page cache"
ha viszont "megfelelő flaggel megnyitni a fájlt, hogy írás esetén amint lehet írjon diskre is és addig ne térjen vissza amíg nem írta ki" ugyanúgy megmondja, csak itt nem egy explicit fsync hívással, hanem egy write-tal.

Igen, egy fájlkezelő protokoll, amivel pld. fájlokat lehet feltölteni, és az nginx-es implementációjában nem tudsz meggyőződni arról, hogy az valóban tárolásra került-e, vagy sem.

Lerajzoltam hogy én hogyan értelmezem a dolgokat, hátha segít.

A jelenlegi megoldás, az OS intézi a fileok syncelését.

Fsync hívása async módon, ez olyan sokat nem segít.

Fsync hívása sync módon. Ez ugyan biztosítja, hogy csak akkor kapsz választ amikor a file a disken van, de cserébe eléggé költséges.

Megjegyzés: nyilván az írási műveletek átfedhetik egymást, a diagramokon csak az egyszerűség kedvéért nincs átfedés.

Lépjünk tovább egy kicsit, mert ezek még jobbára logikai válaszok!
Nem tudjuk sem az oprendszert, sem a hardvert. (Jó, biztosan linux.:))
Azt sem tudjuk, ha mégsem sikerült az írás, vagy csak "kicsit nem sikerült", akkor mi a teendő.
Mi van akkor, ha sikerült az írás, de egy mikromásodperc múlva (ahogy a Terminátorban is mondják) megsemmisül a gép, de szeretnénk a továbbiakban felhasználni a feltöltött adatokat.

Először is az látszik, hogy egy webszerver alapvetően nem ilyen tranzakciókra készült. Ezért nem csoda, ha nem akarnak ezzel foglalkozni. ;)

A file igazán csak akkor "van ott", ha vissza is lehet olvasni. Erre a verify felkapcsolása az igazán rigorózus módszer. Ráadásul nem csak az adattartalom, hanem a hozzátartozó bejegyzésnek is hibátlannak kell lennie a logikai visszaolvasáshoz!

Nem mindegy a diszk sem, vagy az extra cache megléte sem. Ugyanúgy a tükrözés is számít.
Az egyes fícsörök megléte és használata jelentősen befolyásolja az egyedi és csoportos tranzakciókat. Az összes lehetőség kézbentartásával befolyásolható a tranzakciók sebessége, természetesen a megbízhatóság rovására. A performance esés szorzója elérheti az 50 értéket is.

Megint kérdéses, ha pl. RAID1 esetén kiesik az egyik diszk, akkor mi legyen a stratégia?

Nem értem, hogy jön ez ide mind, amit írsz?

"Nem tudjuk sem az oprendszert, sem a hardvert. (Jó, biztosan linux.:))"

Teljesen mindegy, nem? Persze, az fsync() teljesen másképp viselkedik consumer és server grade hardveren, de az most nem ide tartozik.

"Azt sem tudjuk, ha mégsem sikerült az írás, vagy csak "kicsit nem sikerült", akkor mi a teendő."

HTTP error 500-at adunk a kliensnek, és kész.

"Mi van akkor, ha sikerült az írás, de egy mikromásodperc múlva (ahogy a Terminátorban is mondják) megsemmisül a gép, de szeretnénk a továbbiakban felhasználni a feltöltött adatokat."

Ha megsemmisül a gép, akkor nem szeretnénk róla semmilyen adatot felhasználni.

"Először is az látszik, hogy egy webszerver alapvetően nem ilyen tranzakciókra készült."

Tökre ilyen tranzakciókra (is) készült, proxy felhasználási módban nagyon figyelnek is erre. A kliens akkor kap OK választ, ha a mögöttes réteg is megtette ezt.

"A file igazán csak akkor "van ott", ha vissza is lehet olvasni. Erre a verify felkapcsolása az igazán rigorózus módszer. Ráadásul nem csak az adattartalom, hanem a hozzátartozó bejegyzésnek is hibátlannak kell lennie a logikai visszaolvasáshoz!"

Honnan verifikálsz és mikor, ugyanaz alól a kernel alól? Nem sokat ér :(

"Nem mindegy a diszk sem, vagy az extra cache megléte sem. Ugyanúgy a tükrözés is számít."

Teljesen lényegtelen a userspace program szempontjából. Van egy szuper interfész erre, fsync(), megfelel mindenféle adatbáziskezelőnek, queue managernek, stb. Ha a lokális hardveren valamiért rosszul működik, az egy dolog, de ez se nem tervezési, se nem implementációs szempont. Ha degradált állapotban van a gép, és emiatt kivennéd a szolgáltatásból, az megint más kérdés.

Mondjuk a "the bottom line is separation of concerns. Nginx should not use fsync because it isn’t nginx's business" ervet meg el is tudnam fogadni esetleg.

Persze a masik oldalrol nezve meg nem erdekel engem, hogy kinek a felelossege, de legyen az a fajl a diszken, ne egymasra mutogassatok :-)

Illetve kivancsiva tettel: mas web szerverek hogyan intezik ugyanezt?