Díky za názorný popis, hned jsem prošel nastavení na jednom ze serverů:
max_connections = 255
shared_buffers = 1GB
temp_buffers = 500MB
work_mem = 128MB
Paměť serveru je 8G. Moje hodnota max_connections je úlet, způsobený právě chybou v aplikaci (dávno opraveno, jenom max_connection zůstalo). Hodnota work_mem je asi o cosi větší, ale k databázi se připojuje jen jedna aplikace s poměrně jasným portfoliem dotazů a v praxi to nedělá potíže.
U části shared_buffers bych ještě zmínil nutnost nastavit velikost sdílené paměti v operačním systému. V konfiguračním souboru /etc/sysctl.conf jsou to hodnoty kernel.shmall a kernel.shmmax.
Zaráží mě odrazování od persistentních spojení. Vždyť navázání TCP spojení také něco stojí a není to zrovna levné.
"Postgres (stejně jako všechny ostatní aplikace v Linuxu) vrací alokovanou paměť operačnímu systému až v době zániku procesu (v Postgresu po odhlášení uživatele)."
To o těch aplikacích je velmi nepřesné tvrzení. Aplikace samozřejmě může alokovat a vracet paměť systému kdy chce.
Mna skor zaraza toto tvrdenie: "Situaci může zhoršit, když je klient špatně napsaný a neuvolní spojení při svém zániku. Postgres sám o sobě spojení aktivně netestuje, a pokud neposílá výsledky na klienta, tak se nedozví, že klient už neexistuje ... "
To mi pripomina situaciu ked som sa v mladosti pokusal napisat jednoduchy echo server a ignoroval som niektore chyby. Potom sa mi naozaj stavalo to co je v clanku napisane. Povazujem to za skolacku chybu a neverim ze v Postgresse to tak naozaj je. Ked skonci klient, tak druha strana sa o tom takmer vzdy dozvie.
Server nemá žádný mechanizmus, jak se dozvědět, že klient je mrtvý. Používá se klasická unixová architektura - klient startuje komunikaci žádostí, a čeká na výsledek. Server jinak nemá důvod komunikovat s klientem a navíc, pokud klient nečeká na výsledek, tak klient ani neodpovídá (nečte příchozí pakety). Například klient zobrazuje data a čeká na vstup uživatele - téměř veškerá komunikace je synchronní jednoprocesová. Klient vždy startuje komunikaci - tudíž Postgres čeká na příkaz od klienta nebo na zavření spojení. Pád klienta ovšem nemusí zavřít TCP/IP spojení.
Co myslite tym ze klient je mrtvy? Ze skoncil? To bolo napisane v clanku. Ked klient skoncil a nezavrel spojenia, zavrie ich za neho OS vzdy, server sa o tomto dozvie a je to aj v TCP protokole.
Otvorte si dve okna:
1) spustite: nc -l -p 4444
2) spustite nc localhost 4444
2) napiste aaa
1) zobrazi sa aaa
spojenie existuje
2) Ideme nasimulovat ukoncenie klienta
2) Ctrl+Z
2) ps -ef | grep nc
2) Najdes pid klienta
2) kill -9 PID
1) read(net): Connection reset by peer
Server sa dozvedel ze klient skoncil. Klient urcite nezavrel spojenie sam, zabil som ho s kill -9. Spojenia v POSIXE vzdy zevrie bud klient alebo OS.
http://blog.stephencleary.com/2009/05/detection-of-half-open-dropped.html podle tohoto dokumentu není garantováno, že se na proti stranu pošle informace, že se spojení zavřelo.
* http://superuser.com/questions/298919/what-is-tcp-half-open-connection-and-tcp-half-closed-connection
* https://en.wikipedia.org/wiki/TCP_half-open
Fajn ... pridu a vytrhnu ti kabel ... mimochodem, kill je jeste porad vicemene standardni ukonceni aplikace, aplikace muze klidne vytuhnout a prestat komunikovat. Dokonce to muze vypadat tak, ze uzivatel sestreli fontend, ale backend zustane viset, vedle si to pusti znova, funguje, takze to dal neresi. A to nemluve o tom, ze nikde neni receno, ze nejakej paket s ukoncenim spojeni nekam dorazi.
Tam jsou dva problémy - ten, který jsem zmiňoval se děje při neaktivitě - a souvisí s tím, že nějak (teď to neřešme, k postgresu nedojde paket, kterým se signalizuje uzavření socketu.
Druhý problém, s podobným výsledkem, který jsem zapoměl zmínit spočívá v tom, že klient pošle na server dotaz, který se dlouho počítá - a Postgres nemá důvod si olíznout socket. Po pádu klienta Postgres pokračuje ve výpočtu - pokud neposílá výsledky na klienta, tak nemá důvod si kontrolovat stav socketu - průběžně se kontroluje jen příznak canclu - který jde pro tento účel speciálně vytvořeným TCP/IP spojením. Nicméně klient, který havaroval CANCEL už nikdy nepošle - a Postgres bude počítat dokud nespočítá první řádek a nepošle jej na klienta. V tomto případě nepomohou keep alive packety. Existuje patch do PgBounceru, který na zavření klienta dokáže vygenerovat CANCEL.
První problém jsem určitě viděl u klienta 3-4 roky zpátky - ale netuším, na čem to běželo. Druhý problém byl relativně častý u mého klienta, a trvalo několik let, než se jej podařilo uspokojivě vyřešit - což bylo dáno architekturou a rozsahem aplikace. Nicméně tento problém se může relativně často vyskytovat u webových aplikací, které nastartují dotaz a už nepočkají na výsledek (a nevynutí si CANCEL)
OS samozrejme uvolni zdroje ktore k procesu ma (ak zostali otvorene) takze program nemusi nic zatvarat zatvori ich OS, podobne ako file descriptory. Problem je ked padne OS, prerusi sa spojenie na fyzickej urovni (close nedorazi do ciela), proces zije ale je zaseknuty a podobne nehody.
...napisat jednoduchy echo server a ignoroval som niektore chyby. Potom sa mi naozaj stavalo to co je v clanku napisane. Povazujem to za skolacku chybu a neverim ze v Postgresse to tak naozaj je..
My provozujeme jiz 20 let aplikacni server, ktery reaguje partne jak postgresql. Zajimalo by mne, jak to delate Vy. Idealni by bylo, aby server ukonci to spojeni, kdyz 'uz to nema cenu' dal komunikovat. Ale jak se to dovi?
My to mame tak, ze server odsadi read() a ceka na urcity pocet bajtu, ktere prileti po siti. Dokud to mnozstvi neprileti tak se ceka (treba nekonecne). Jestlize uzivatel s databazi prave nepracuje, tak posila klientsky program sam od sebe jednou za 20 minut keep alive , jinak to server sam po 25 minutach utne, protoze ten proces s tim read() ma nastaveny alarm() na tech 25 minut.
Jak to delate Vy? Jak poznate, ze klient uz dlouho mlcel a je treba to utnout - tedy bez nejake predem stanovene prodlevy?
Čas navázání spojení je vidět u sessions a dotazů pod půl vteřiny (možná ještě méně). Výjimkou, která potvrzuje pravidlo, je spojení uvnitř SSL tunelu, které je výrazně dražší. U sessions, které trvají déle než pár vteřin, je už cena za navázání spojení naprosto zanedbatelná (a mohou naopak převážit ty negativní jevy - jako je potřeba většího počtu otevřených spojení).
Aplikace mohou vracet paměť systému, kdy chtějí - to jsem nevyvracel - ale nedělá se to - minimálně aplikace postavené na glibc to běžně nedělají. Nemá cenu vracet systému defragmentovanou paměť. Když zavolám free, tak se paměť uvolní pouze interně - uloží se do evidence obsluhy mallocu - při dalším requestu o paměť se napřed zkouší využít nevyužitá vrácená paměť, a pokud není k dispozici dostatečně velký blok, tak si proces řekne o další paměť systému. Jelikož aplikace nemají křišťálovou kouli a nevědí, jestli paměť bude potřeba nebo nikoliv, tak ji obyčejně systému nevrací. Případné problémy se zbytečnou alokací se většinou řeší nastavením maximálního stáří procesu, workeru, ..
"Vždyť navázání TCP spojení také něco stojí a není to zrovna levné."
Tak nejak. Ono to asi neni ani moc drahe. Ale je to duvod, proc se napriklad do HTTP dodelalo KeepAlive.
Slusne vychovanej program, kdyz uz jednou otevre TCP spojeni, tak ho pokud mozno drzi.
A nemelo by serveru vadit, ze ten program ve fabrice bezi na stovce (na stovkach) stroju.
Ale asi nema cenu to dal rozebirat, naposledy se to resilo tady:
http://www.abclinuxu.cz/zpravicky/postgresql-9.6
Byl tam v diskuzi nejaky odkaz, proc Uber presel k MySQL a jeden z duvodu bylo, ze v Postgresu je jedno spojeni = jeden proces. Takze to beru jako vlastnost a budu s tim nejak zit.
Ale tak me napada, jestli by nemohla existovat (najit si sve misto na svete) nejaka brana, ktera by prijimala a drzela ty stovky spojeni a s postgresem komunikovala pouze pomoci nejakeho nizsiho poctu. (Asi by tam musel byt nejaky hack s transakcemi.) Ale je to asi zbytecne, protoze ve vysledku je skoro jedno, jestli se klient nepripoji, nebo nedocka vysledku. Takze prestavam teoretizovat a jdu nakrmit prase.
Pokud potřebuje držet spojení, nebo potřebujete mít stovky neaktivních připojení do Postgresu, tak je tu pgbouncer nebo pgpoolII http://pgbouncer.github.io/, https://wiki.postgresql.org/wiki/Pgpool-II - možná další. Samozřejmě, že můžete používat aplikační pooly - spojení pooluje PHP, Java, ..
> Ale tak me napada, jestli by nemohla existovat (najit si sve misto na svete) nejaka brana, ktera by prijimala a drzela ty stovky spojeni a s postgresem komunikovala pouze pomoci nejakeho nizsiho poctu.
Neřeší toto ve článku zmíněny PgBouncer? Případně vím, že existuje pgpool, který si myslím, že konexe pooluje, ale nevím jak a hlavně jeho využití je při nasazení do clusteru (HA).
Aplikace samozřejmě může vracet paměť kdy chce, a PostgreSQL to samozřejmě taky dělá, snad s výjimkou pár cachí do kterých se ukládají metadata o tabulkách apod. a věcí jako jsou fulltextové slovníky. Nevím jak to Pavel myslel, jediné co mne napadá je že narážel na to jak paměť spravuje glibc interně - tam se většinou děje přes sbrk a kernelu se to vrací skutečně až při ukončení procesu. Pokud vím tak například pokud po malých kouscích (aby to nešlo přes mmap) 1GB, a pak uvolníte všechno kromě posledního kusu, proces si pořád bude držet 1GB. Teoreticky by se o to měla postarat virtuální paměť, no ale úplně ideální to není.
Spíš bych řekl že horší je overhead spojený s častým forkováním procesů a navazováním TCP spojení, ale hlavně nebezpečí že se probudí víc těch procesů a začnou se tahat o zdroje - vysoké max_connections hodnoty jsou časté v situacích kdy se k DB připojuje víc aplikačních serverů s lokálními connection pooly. Ty sice řeší ten fork/TCP overhead, ale předpokládá se že aktivních je jenom pár těch spojení současně (tj. běží jenom pár SQL dotazů současně) - ale stačí jedna chybka v aplikaci neštěstí je hotovo.
Nevím jak to Pavel myslel, jediné co mne napadá je že narážel na to jak paměť spravuje glibc interně - tam se většinou děje přes sbrk a kernelu se to vrací skutečně až při ukončení procesu.
Řekl bych, že to myslel přesně tak, jak to napsal: „Postgres […] vrací alokovanou paměť operačnímu systému až v době zániku procesu […].“ Takže je to stručnější verze toho vašeho vysvětlení.
No, otázka je co znamená "vracet operačnímu systému", že? Protože jak PostgreSQL tak většina ostatních aplikací to vrací jenom glibc, a je otázka co s tím dělá glibc. Takže aplikace si můžou vracet co chtějí, ale kernel se o tom nemusí dozvědět.
Což ale znamená že Bukoň nemá tak úplně pravdu, protože ostatní aplikace se chovají stejně jako PostgreSQL (který samozřejmě průběžně volá free() během zpracování dotazů a nedrží tu paměť zbytečně dlouho).
Vracet operačnímu systému znamená vrátit paměť systémovým voláním kernelu, který ji předtím aplikaci poskytl. glibc není operační systém, glibc je jen běžná knihovna v uživatelském prostoru – a program ji ani nemusí používat. Takže když programátor ve své aplikaci používající glibc zavolá free(), aplikace (jejíž je glibc součástí) operačnímu systému nic nevrací. Pouze si ji označí jako nepoužívanou a při příštím požadavku na alokaci nebude žádat o novou paměť operační systému, ale použije tu nepoužívanou (pokud to jde).
Libc rozhodne neni bezna knihovna. Libc je to, co v unixovem svete poskytuje rozhrani operacniho systemu. Rozhrani mezi programem a OS je definovano na urovni volani libc (viz POSIX), nikoliv na urovni volani jadra. Hranice mezi userspace a kernelspace je z tohoto hlediska irelevantni.
Prave proto se take OS kernel oznacuje jako 'jadro', protoze cely OS zahrnuje i 'obal' (libc, shell).
Takze kdyz programator zavola free(), vrati pamet operacnimu systemu. Ze jedna komponenta OS (libc) ji jeste nemusi vratit jine komponente OS (jadro) a tedy ta pamet nemusi byt dostupna pro ostatni, je jina vec.
Tady už zabředáváme do slovíčkaření - důležité je, že paměť, kterou si alokuje proces, je často alokovaná procesu až do jeho konce. A pokud proces žije extrémně dlouho, tak tuto paměť nemůže použít nikdo jiný. V případě Postgresu - ani jiné připojení do Postgresu (protože sessions jsou v pg implementovány jako procesy). I proto je dobré se vyhnout extrémně dlouhým sessions.
Není jen unixový svět, není jen jediná implementace libc
, a není nutné libc
vůbec používat (ponechme stranou, zda je to rozumné). Pokud se operačním systémem nemyslí jen jádro, zahrnuje to mnohem víc než jen libc a shell, obvykle se tím myslí i GNU utility a další. Takže „vrátit paměť operačnímu systému“ znamená vrátit ji jádru, určitě paměť nevracíte shellu nebo taru.
Z uvedeného o navracaní či skôr nenavracaní pamäte a nevhodnosti dlhotrvajúcich pripojení sa javí client-side connection pooling ako krajne nevhodné riešenie, tak? Ako je na tom Postgres v prípade, že klientom je napr. Java aplikačný server (WebLogic, GlassFish, WildFly, ...), kde aplikácia beží týždne až mesiace cez jeden connection pool, ktorý udržiava a recykluje uzatvorené spojenia?
Řekl bych, že je tam možnost nastavit maximální stáří spojení - maxLifetime např. https://github.com/brettwooldridge/HikariCP - nejsem Javista - takže moc toho nevím, ale měl jsem zákazníka s aplikací v Javě, a vím, že něco jako max life time nastavoval a pomohlo mu to.