No cas vyvojaru ve srovnani s tim, co to stoji na strane sysadminu teda docela bledne.
1] konfigurace kvuli extra portu na firewallu
2] udrzba pgbounceru etc
3] nepodpora replikacniho protokolu via pgbouncer
4] reload pg_hba vyzaduje extra akci reload pgbouncer
5] autentifikace uzivatelu + logy
6] monitoring pgbounceru
apod...
Ono se to nascita...
To je historicky dané. Postgres connection pool neměl - víceméně se předpokládalo, že pokud bude potřeba, tak se použije aplikační connection pool - PHP, Java, a smyslem bylo snížení režie connectu, nikoliv overloading spojení, který vůči Postgresu nedává moc smysl, jelikož Postgres nemá řízení zdrojů, prioritizaci zdrojů. Pro aplikace, které vlastní pooling neměly, tu byl dostačující pgbouncer. Bohužel pooling v Javě je takový, že než se aplikace rozjede, tak si vezme 200 spojení, a pokud máte 5 aplikačních serverů, tak máte 1000 spojení do pg (z toho je reálně použito 10), ale Javovský pooler refrešuje a prostřídává všechny, takže to generuje dost zbytečné režie, a tak je potřeba ještě jeden pooler, který dokáže tuhle režii eliminovat. pgbouncer je relativně hodně lehký, ale u pár tisíc spojení se začne zadrhávat, jelikož jede v jednom vlákně. Další poolery zvládají výrazně větší zátěž, ale mají samy větší režii než pgbouncer.
Další věc je ta, že Postgres nemá podporu vláken, a napsat pooler bez vláken na stávající architektuře dost dobře nejde - jinak skončíte jako pgbouncer na výkonu jednoho CPU. Dneska už bude situace jiná, ale před 15-20 roky, kdy bylo víc prostoru na architekturu, tak nebyla API pro management vláken na těch platformách, kde Postgres bežel. Teď to bude jednodušší, jelikož většina starých Unixů už je nepodporovaná.
Jen jestli to není řešení problémů na nesprávném místě. Jak se stane, že se do DB přihlásí více klientů, než je max_connections? Neměl by se řešit počet klientů už na vrstvě nad tím?
Všechny příklady, které jsem viděl v praxi, vždy znamenaly problém někde jinde. Přidání pg_bounceru nepomohlo, protože se tím někde jinde zvýšila latence. Takže pokud už nějaká vrstva nad DB ví, že klientů je připojených už hodně, má si to s klientem vyřídit už tato vrstva. A ne to nechat propadnout až na úroveň db a potom tu chybu nebo tu latenci nechat zase probublat zpět.
Tak to je stejne u kazde klient/server app ne? Proc asi haproxy, nginx, apache, oracle a mnohe jine aplikace umoznuji pripojit (neznamena apriori prihlasit!) vic spojeni nez je limit backendu? To radeji tu latenci, nez tvrde odmitnuti spojeni, coz treba vetsinou znamena nove navazani spojeni (o milisekundu pozdeji? vterinu? minutu?) nebo rovnou error aplikace.
To není jen umožnit spojení. Aby to fungovalo, tak musíte mít i nějakou prioritizaci. Databázové operace jsou dost paměťově náročné - o několik řádů víc než vygenerování html stránky. Databáze je mnohem víc limitovaná pamětí - nechcete naráz provádět příliš mnoho dotazů, jelikož pak se nevejdete do paměti a začnete používat swap a je to v háji. Když tam pustíte přiliš mnoho uživatelů, tak se vám může stát, že žádný už se nedočká výsledku, nebo že stovky nedůležitých úloh blokují důležité úlohy. Aby to mohlo trochu fungovat, tak potřebujete prioritizaci přidělování zdrojů, exekuci ... Podívejte se na Vertiku. To není jednoduché naprogramovat, a není to jednoduché ani nakonfigurovat - a je to dost mimo běžné nasazení Postgresu. Postgres vám garantuje, že když už se připojíte, tak můžete okamžitě spustit dotaz, a dostane veškeré zdroje, které mu dá systém. Je to jednoduché, a pokud je max_connection nastavený realisticky (vůči tomu co dá hw), tak se vám Postgres bude chovat rozumně.
Oracle to ma stejne. Pocet spojeni je parametr, ktery urcuje velikost datovych struktur uvnitr databaze, a tenhle parameter se neda za jizdy zmenit. (Shared server vynechavam). "Normalni" SpringBoot aplikaci(OTLP) by mel stacit connection pool, ktery ma nizsi destitky spojeni. A na strane dabaze muze byt limit na pocet spojeni do tisice.
Navic na limit na pocet DB spojieni jsou navazany i kernel parametry na pocet otevrenych souboru, pocet zamku, ..., ktere DBA vetsinou neresi, protoze na to nemaji prava.
Problem je se starymi Java aplikacemi, ktere reaguji na kazdou neocekavanou vyjimku tak, ze se thread ukonci a konexe uz se nikdy do poolu nevrati. To pak vede situaci ze jsou vsechny limity na spojeni neumerne velke. To ale neni reseni ale jen w/o. Oracle na to "vymyslel" feature v connection poolu UCP, ktery umoznuje zabijet i konexe, ktere ma aplikace vypujcene, ale uz nekolik hodin je nepouzila.
Jedina DB o ktere vim ze to nejak resi je Infoxmix - ten je ale mrtvej. Ten bezi multivlaknove a navic ma vlastni scheduler vlaken, takze muze jedno vlakno behem vykovanani SQL dotazu "preskocit" a pokracovat ve vykonavani jineho dotazu.
Je to "reseni" na tema vyvojar aplikace je dobytek, ktery netusi ani zbla jak funguje databaze. A tak neni schopen jakkoli premyslet nad tim, ze otevirat do ni tisice a tisice konexi je vazne blby napad.
A presne proto ceka od te databaze, ze si to nejak prebere a vyresi ona.
S pouzivanim vsemoznych frejmworku se cely problem jen multiplikuje, protoze dobytek krat dalsi dobytek krat dalsi a dalsi ... a ten navrchu, co taha nejake ty boxiky ani netusi, ze pod tim nejaka databaze je.
Pak to vypada tak, ze ja jakozto neprogramator nactu milion zaznamu do gridu za sekundu (z te databaze), zatimco ten "vyvojar" jich tam nacte za minutu 50 ... a vic nez 200 uz neumi, to uz se mu to cele zhrouti.
Alternativne (ono to velmi souvisi) ... Diablo 4 ... nejakeho dzenia napadlo, ze vzdy kdyz potkate hrace, posila vam server obsah jeho beden(ne ze by ste se mohli podivat, jen tak pro srandu ...). Laguje to jak svin, a presne timto argumentovali to, ze tech beden nemuze byt vice ...
Není to jen o tom, že vývojář je dobytek. Co když aplikace používá autoškálování a to v neočekávaném peaku přestřelí všechny meze. Nebo ještě líp sejdou se peaky z různých služeb nad stejnou databází, ve chvíli, kdy ty meze byly dobře nastavené. V tuhle chvíli asi neni žádný dobrý řešení, ale v dnešní době bych tipoval, že problém nebude jen u dobytků programátorů. Je potřeba se na celý problém dívat víc defenzivně a nespoléhat na dobré nastavení žádné aplikace. V tomhle mi přijde dobrá kombinace pgbouncer + dobře nastavený pooling v aplikaci.
Pridam aj moj nazor. Za mna je jedno z rieseni (elegantnejsie, ale s vyssim narokom na programatora a asi aj na udrzbu kodu) pouzit "reaktivny" sposob db. Napriklad cez r2dbc drajver. Ale na toto tiez postgresql nema "nativnu" podporu...
Vychadzajme z podstaty veci. Diskove (aj sietove) I/O je obmedzene. Rovnako ako aj CPU (tu sa to jemne meni, ale stale neni 1000 jadier beznych). Preco by som sa mal snazit obsluzit 1000 paralelnych zapisov (albo citani) na disk, ked viem, ze hlavicka tam je jedna a fyzika umoznuje jeden zapis naraz (alebo napriklad 10).
A odskocime si k threadpoolu v starom apache a k dovodu preco vznikol JEDNOVLAKNOVY nginx, ktory na to siel inak... Rovnako ako doom. S jednym vlaknom s prehladom simuluje niekolko NPC (pocitacom ovladane postavy v hre), audio, rendering, riesenie simulacie hry a tiez nacitavanie I/O klavesnice...
Osobne som pouzival v jednom PoC (proof-of-concept) r2dbc spolus s project reactor a de-facto v jednom vlakne odbavoval stovky/tisicky requestov za sekundu. Ano, rovnake cislo sa da odbavit aj bash skriptom cez xinetd. Otazkou je, kolko recourceov to zralo...
K jakykoli aplikaci muzu pristupovat dvema zpusoby.
1) neresim to, a kdyz pozadavky prekroci moznosti, tak se to proste nejak nedefinovatelne slozi. To je naprosto regulerni pristup u hromady vsemoznych treba webu, specilene jako fckbook a podobne. Kdyz to na par hodin umre, nic se vlastne nedeje. Dokonce bude hromady PR zadarmo, protoze se o tom bude vsude psat.
2) ocekava se, ze aplikace pobezi vzdy, a v takovym pripade proste kazdy jeden clanek retezu ma s nejakou rezervou navrzene a nastavene neprekrocitelne limity. Takze kdyz reknu ze limit je 1000, tak 1001 ma proste smulu. Vzdy. Ale tech 1000 vesele dal bezi.
A ta dobře navržená infrastruktura vypadá jak? Že přežije každodenní změny včetně vypínání a zapínání služeb s nutností podpory pro rollback nebo statická konfigurace co jednou někdo navrhne a pak už se na to nesmí šahat? Dobře navržená infrastruktura dneska jednoznačně musí předvídat. Nejsou žádné pevně dané limity a proto je potřeba řešit i neočekávané stavy.
Ono frontování spojení může vytvořit docela nehezký deadlock. Problém je, že aplikace neví jestli je spojení aktivní nebo ne, a pooler zase neví, co beží v daném spojení, a jaké jsou jeho závislosti. Aplikace sice provádí health check, ale pokud si spojení nezamkne transakcí (což samozřejmě nedělají), tak hned další příkaz už může poslat do spojení, které je už přesunuto do čekací fronty. Pokud nejsou dependence mezi transakcemi, tak by to tolik vadit nemělo, ale ony někdy jsou, a pak to může dělat dost nehezké problémy. Frontování spojení vám může pomoct, ale zrovna tak uškodit - bez prioritizace.
Kdyz se na to ptas, za takovy veci se plati ... v desitkach mega.
Infra se navrhuje bud na zaklade nejakych vypoctu, nebo na zaklade benchmarku, A pak mas vzdy a vsude nejaky fixni neprekrocitelny limit. Je totiz mimochodem radove levnejsi, mit HW ktery odmita pozadavky presto, ze zrovna nic nedela, nez resit, ze se neco zhroutilo proto, ze se to ten dalsi pozadavek obslouzit pokusilo, a to zrovna ve chvili, kdy prisla hromada podlimitnich requestu.
Mam takovej krasnej priklad z praxe jednoho korporatu ... neresili to, a pak se jim stavalo, ze prisel zakaznik do obchodu, a na to aby se dozvedel jesli maji neco skladem, cekal treba taky 20 minut, protoze HW zrovna obsluhoval pozadavky, ktere mel fuckovat.
Vzdycky mas totiz nejaky konecny pocet IOps, konecny pocet GHz, koneckou velikost ram ...
Takova infrastruktura neexistuje. Vzdy tam pribyde nejaky novy proces, temer nikdy neubyde.
Vezmu si jeden z nasich pripadu. Pan Stehule u nas byl na konzultaci, tehdy doporucoval mit max 10 spojeni na 1 cpu jadro. Mame server, ktery ma nastaveno 500 spojeni, protoze je na nej pripojena hromada web a compute serveru. Pri 500 spojeni by to bylo 50 cpu jader...a to v dobe, kdy neexistovaly AMD Zen. Tech 50 cpu jader odpovidalo zelezu se 4 cpu...Samozrejme, museli jsme to doporuceni prekrocit.
Pgbouncer nam omezuje, aby jeden z klientskych serveru mohl zablokovat velkym poctem spojeni databazi. Protoze sice lze na roli nastavit limit, ale to klienta odrizne natvrdo. A pak kdyz chodi ruzne spicky (ruzne akce, posilani emailu, prepocitani dat, ruzne mnohe transakce, web), tak se to proste potka. A extremne predimenzovanou infrastrukturu samozrejme zakaznik nezaplati.
Vsude, kde se clovek podiva, vsude je nejaky fyzicky limit (silnice, chodniky, trubky etc). Vzdy dojde k zpomaleni "provozu", posklada se to tam, nikdy se to "nevyhodi".
15. 5. 2024, 08:51 editováno autorem komentáře
max 10 spojeni na 1 cpu jadro
Je potřeba se zamyslet nad tím, z čeho tohle doporučení vychází. Tohle předpokládá, že se v té DB bude skutečně něco počítat. Pokud v DB nic nepočítáte, tak jednak se nemusí nutně používat PG, ale taky by to doporučení vypadalo jinak. Třeba podle toho, kolik tam máte disků. Klasické rotační disky více než jednoho klienta stejně nezvládnou (protože jedna hlavička), takže nemá smysl nad 10 disky nastavovat 500 klientů. Jen to zbytečně zvýší latence. A ve skutečnosti je to zvýší mnohem více, než to vypadá. Protože jeden rotační disk má pro jednoho klienta opravdu velmi vysoký výkon. Ale už se dvěma to padá mnohem níže než na polovinu. Takže zaplavit "rotačák" mnoha klienty je stejně blbost.
Právě proto je lepší, jak už tady bylo psáno, nastavit ty limity velmi rozumně dle HW pod tím, a aktuální nedostupnost toho HW si vyřídit jinde.
Třeba tak, že příklad: mám db; nad tím apache se všema modama pro python, php; nad tím je nginx. DB má nastavení počet connexí, apache má nastaven počet workerů menší než počet conn do db. Nginx, při nemožnosti se dostat na apache, tak klientovi pošle statickou stránku. A ta statická stránka může být generovaná. Takže "jednou za 10 minut" se vygeneruje statická stránka s nějakými informacemi a tyto informace klient dostane v případě, že backend nestíhá. Takže všechny vrstvy jedou v nějakém svém pohodlném režimu. DB rozumně využívá HW (mnohem lépe, než při absurdním počtu připojení), apache je nastaven dle db a dle cpu a nginx dělá to, co mu jde nejlépe, tedy servíruje statický obsah. A ten statický obsah se dynamicky mění ;-). Jednou za "hodinu".
Vsude, kde se clovek podiva, vsude je nejaky fyzicky limit (silnice, chodniky, trubky etc). Vzdy dojde k zpomaleni "provozu", posklada se to tam, nikdy se to "nevyhodi".
Tahle analogie vůbec nefunguje. Stačí se podívat třeba na elektřinu. Velká zátěž, snížení frekvence, rozfázování sítě, globální blackout. Proto je nutné v nějaký případech části sítě odpojit.
To doporučení vychází z vlastností Postgresu - každý připojený uživatel může spustit dotaz, který se začne okamžitě provádět. Ješte u desetinásobku běžících dotazů je zkušenost, že ten server se zpomalí, ale většinou úplně ne patologicky (záleží, co to bude za db). Pokud těch běžících dotazů bude víc, tak se můžete dostat do zátěže, že se k serveru nepřipojíte a budete muset udělat tvrdý reset (což vám může poškodit filesystém (zase v závislosti na kvalitě komponent v IO). Poškozený filesystém znamená, že můžete přijít o data. Jde primárně o to, jak se ten systém bude chovat při přetížení - jestli se nenechá přetížit nebo se utaví.
Realita je široká. Jsou aplikace, kde všechno vám běží s jednou prioritou, a případné čekání connectu ve frontě nic neudělá. Jsou aplikace, kde je to horší. Jsou aplikace, kde to třeba působí problémy, ale když aplikace neběží, tak se vám zastaví byznys, ale nenaskakují penále, a když to není moc často, tak to až tak moc neřeší.
Nedávno jsem viděl problém, který byl asi způsobený krátkodobým kousnutím síťového disku. To by samotné db nevadilo, ten zásek byl cca na 10sec. Větší problém byly Java servery, které si okamžitě otevřely nových 400 spojení - jelikož to bylo sesynchronizované, tak to dalo db docela dobrý kopanec. A pak stejně aplikace umřela jelikož narazila na max_connection. Mám pocit, že pak ještě ty aplikační servery šly do restartu (protože těch 400 spojení bylo alokovaných ale nepoužitých, a další už se vytvořit nedala). Výsledkem byl 10 min výpadek. Primárně způsobený použitím "chytrých" HA technologií.
Pak také, do určitého počtu souběžně zpracovávaných dotazů se databáze chová lineárně - a nad něj exponenciálně. 10xCPU je primitivní heuristika, která docela dobře funguje. Záleží samozřejmě na IO, velikosti RAM, velikosti db, náročnosti dotazů, ... Při malém souběhu režie zámků bude zanedbatelná, a je i menší šance na zámek, jelikož o výkon IO se dělí menší počet dotazů. Při velkém souběhu už mohou být problémy se zámky, s nimi je už spojená nějaká režie CPU (a třeba SIREAD locky si vezmou CPU dost (pokud je jich hodně)). A CPU pak chybí, a celé se to zauzlovává. Při malém souběhu máte dost velkou šanci, že data ke kterým opakovaně dotaz přistupuje se najdou v share buffers. Při velkém souběhu ta šance klesá, a naopak roste režie s managementem cache, roste riziko vynuceného checkpointu, což může navíc zvyšovat zápis do wal ..
"10xCPU je primitivní heuristika"
Pokud si pamatuju, tak treba u mssql primo ms uvadel, ze treba pro 50 uzivatelu je treba minimalne 1500 iops (coz znamena minimalne 15 7k disku napr - ten pocet je samozrejme bez raidu, takze realne vic). A takovych cisel je samozrjeme cela rada,
A pak samozrejme je zahodno nastavit ten limit na tech 50. Coz plati kdekoli v retezci cele infrastruktury.
Databazi muze bez problemu do kolen poslat jedine query. Jen se typicky neocekava, ze nejaky bastlic si sam ze sve iniciativy bude nejake psat, a ocekava se, ze ti kteri je pisi alespon tusi co delaji. Bohuzel se pravidelne ve sve praxi setkavam s opakem.
"Takova infrastruktura neexistuje"
A to je jako argument o cem a kcemu? Neboli ... je to uplny blabol. Kdyz pocet aut na silnici prekroci X, tak se provoz zastavi. Uplne a navzdy. Ucelem limitu je zaridit, aby se presne toto nikdy nestalo. Nekde se pak saha treba k takovym opatreni, ze v liche dni muzou na silnice liche SPZ a v sude ty sude ze?
Presto tupi ridici jedouci po kruhaku (nebo po kbelske na krizeni s veselskou) davaji "prednost z prava" ... a zpusobi (lokalni) zastaveni provozu + typicky nejakou tu nehodu.
Kdyz zakaznici planuji akce, u kterych se ocekava nejake relevantni navyseni provozni zateze, tak prijdou zamnou, a resi se, zda to lze nebo nelze obslouzit bez nejakeho docasneho navyseni HW.
A kazdy limit se samozrejme navrhuje na zcela konkreni zatizeni a konkreni aplikacni vyuziti. Zadne univerzalni cislo ... neexistuje.
Takovy solidne hardcore priklad? Ok ... Minecraft. To je nejaky server a ... databaze ze? Takze co, budu cekat az se zhrouti ta databaze nebo reknu ze max X hracu? A jak rikam, je to hardcore, protoze na 100 hracich si vpohode vylame zuby HW za 1/2M. Aby to tech 100 hracu s mnoha ingame limity (kdo zna tak treba kravarium, redstone ...) udrzelo (a dalo se to hrat), tak to melo 1TB ram, 32 jader a 8x R10 SSD.
Tohle se snaze resi na vyssi vrstve nez v databazi. ConnectionPool muzete uvnitr JVM celkem snadno najit a vas aplication readiness probe pak muze uznamit loadbalanceru at na konkretni server neprehazuje dalsi pozadavky.
Pokud ale (REST) aplikaci skutecne dojdou spojeni z poolu, tak se muze bud zablokovat a cekat, anebo muze vratit error kod HTTP 503. A pak uz je to na volajicim. V strane volajiciho muzete pouzit knihovnu Hystrix anebo Resilence4J, ktere vam napozadi implementuji re-try mechanismus bez nejake vetsi namahy.
Pokud vam dojdou prostredky na strane databaze, tak je lepsi tu situaci eskalovat k vyssim vrstvam, nez spolehat na nejaky pooling na strane databaze. Zazil jsem i situace, kdy aplikace logovala vse do DB(nesmysl) a pote co byl odstranen problem(blokace procesu), tak byla databaze jeste 3 hodiny na 100% vytizena, protoze ve fronte byly vsechny chybove hlasky, ktere bylo "nutne" potreba zapsat do databaze.
V plánu to je - v commitfestu je patch od Petera E https://commitfest.postgresql.org/48/4972/