Ki a hülye, én vagy te?

 ( bra | 2018. március 5., hétfő - 11:34 )

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ás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

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.

"Note that this is a request only; it does not wait for I/O completion."
szóval itt megint nem játszik.

Pont az a lényege :) Minden aio hívás így működik (async), az eredményre feliratkozol/rákérdezel/stb.

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 :)

Hát nekem sincs, ezeket a C-s dolgokat meghagyom az öregeknek.. de inkább libeventtel vagy hasonlóval szokták (eggyel magasabb szinten) megoldani a callbacket.

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.)

De, garantált, ezt az OS elintézi neked.
Ha lenne fsync, és nem lenne sikeres, nyilván adna egy 500 internal server errort (vagy más, releváns HTTP error code-ot), és sikertelen lenne a feltöltés.

Akkor konzisztencia probléma lehet? A levelező listán is volt egy ilyen válasz.

Feltöltesz valamit, visszakapod, hogy OK, majd valamiért meghiúsul a tényleges művelet (elhasal a gép, a kernel, bármi), a fájlod pedig elveszett.

Nem teljesen értettem mit nem értenek rajta, mitől veszik biztosnak, hogy a buffer fel is dolgozódik, ezért ezt kizártam, de mostmár értem.

Ha a fájl a memóriában már elérhető, akkor onnét propagálja az OS ha hozzá akarsz férni - tehát nem attól függ hogy volt-e sync vagy sem.

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#terminology-concepts
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?

Mégegyszer: a probléma az, hogy a sikeres HTTP PUT azt jelentse, hogy a fájl biztonságban van. Nem, nem megoldás.

Az nem jó megoldás neked hogy sync opcióval csatolsz neki egy külön partíciót csak erre a feladatra?

Nem, mert az tényleg nagyon durván lassú lesz: amíg egy sync write fut, az egész nginx nem tud mást csinálni, mivel egyszálú. Nyilván lehet sok processzt indítani, de azok is egyszálúak lesznek, így a vége már moderált terhelésnél is kisebb katasztrófa lesz.

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, ott a filesystemes gyerekek hisztiznek az O_PONIES izével, hogy az nem az ő dolguk :)

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-M0daf2d114ef03393a315954b

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.

Ebben az elképzelt világban hogy működik egy adatbázis commit művelete?
Csinál egy write()-ot valami fájlba, visszaadja neked, hogy a tranzakció OK, majd valamikor az OS úgy dönt, hogy tényleg OK lesz?

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.

Igen, értem, hogy ezt gondolják. De ha egy fejlesztő, aki rendszerközeli kódot ír, nincs tisztában azzal, hogy működik egy OS, meg fájlrendszer alatta, az szerintem gáz.
És amikor azt mondja, hogy a probléma megoldása, hogy vegyél "reliable server"-t, akkor csak sírni tudok.

Ott se kézzel fsync-elni kéne, hanem 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.
De jelen esetben a WebDAV egy fájlküldő/kezelő protokoll, ennyi erővel tiltsuk le teljesen a page cachet...

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.

Szerintem hasonló, nem annyira egyszerű problémák megoldására és az erőforrások összehangolására született a kernel ;)

Kicsit konkrétabban mit is szeretnél ezzel mondani? Mert információtartalma ennek sajnos nincs. :)

Bocs :) Amúgy meg-patch-elni nem akarod az nginx forrást fsync-kel? Talán nem olyan bonyolult.

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?

Nem tudom, nem néztem meg.

Apache kapcsán kapásból érdekelne (hiányos vagyok ahhoz hogy utánanézzek), érdekes fordulat lenne az nginx vs apache "harcban", újabb odavágható "érv" lenne.

Milyen apache vs nginx harc? :)