Connection limits - proof of concept
Čas od času se někdo v mailing listu zeptá jestli existuje způsob jak omezit počet spojení podle IP adresy, databáze nebo uživatele. Ne, nic takového v core implementováno není, ačkoliv by to byla velmi užitečná vlastnost zejména clustery sdílené více uživateli (aplikacemi, zákazníky, ...). Podobné quoty lze někdy dostatečně implementovat pomocí connection pooleru jako je pgbouncer nebo věcí jako je netfilter connlimit, ale obě řešení mají určité nevýhody.
Ale! Máme přeci věcičky kterým říkáme "extensions", "hooky" a "sdílené knihovny." Minulý týden jsem napsal jednoduchý "proof of concept" extension která používá "client auth hook" a umožňuje vám nastavit omezení počtu spojení podle různých kritérií. Není to dokonalé (viz. dále), ale funguje to celkem pěkně. Zatím je dostupná na githubu a až opravím zbývající drobnosti tak ji publikuji na pgxn.
PS: Díky Magnusovi za upozornění že existuje "client auth hook" který by se možná dal použít, a TL za upozornění jak neuvěřitelně ošklivý hack byla původní verze extension.
Postup instalace je celkem jednoduchý a je popsán v README, takže ho přeskočím.
Existují dva zdroje limitů spojení - GUC proměnné a pravidla. GUC proměnné jsou definovány na obvyklém místě v postgresql.conf, zatímco rules jsou uloženy v novém souboru pg_limits.conf (v datovém adresáři). Mějte na paměti jednu věc - pravidla nejsou aplikována na superuživatele, takže se nikdy nestane že by se kvůli nim superuživatel (DBA) nemohl přihlásit.
Poznámka: Byl jsem upozorněn že jak CREATE USER tak i CREATE DATABASE mají volbu CONNECTION LIMIT, pomocí které lze omezit počet spojení dle uživatele a databáze. To je v mnoha případech nepochybně postačující, nicméně pro omezení dle IP adresy nebo komplexnější pravidla to dostačující není.
GUC proměnné
GUC proměnné definují "výchozí limity" platné pro všechny uživatele, databáze nebo IP adresy. Například toto nastavení
connection_limits.per_database = 20 connection_limits.per_user = 5 connection_limits.per_ip = 10
znamená že nikdy nebude existovat více než 20 spojení pro žádnou databázi, více než 5 spojení pro žádného uživatele nebo 10 spojení pro IP adresu.
pg_limits.conf
Ale co když potřebujete komplexnější limity, jako například "tento uživatel může otevřít 5 spojení do databáze A a 10 spojení do databáze B"? No, k tomu je pg_limits.conf. Formát připomíná pg_hba.conf, tj. na každém řádku jsou 4 nebo 5 hodnot
database user ip [mask] limit
a specielní hodnota "all" odpovídá libovolné hodnotě. Takže například pravidlo
all myuser all 10
je skoro jako "per_user = 10" až na to že je aplikováno pouze na uživatele "myuser." IP maska může být zadána dvěma způsoby, stejně jako v pg_hba.con - například tato dvě pravidla jsou ekvivalentní
all all 127.0.0.1/32 5 all all 127.0.0.1 255.255.255.255 5
tj. z localhostu může existovat maximálně 5 spojení.
Note: Místo IP adresy lze zadat hostname (např. "localhost") - pravidlo bude naparsováno ale zatím nebude kontrolováno. Toto je jedna z věcí které je třeba dořešit.
GUC proměnné vs. pg_limits.conf
Oba zdroje lze samozřejmě kombinovat - definovat výchozí hodnotu a následně ji předefinovat pro vybrané uživatele. Pravidlo z pg_limits.conf má vždy vyšší váhu, takže například po zkombinování této GUC proměnné
connection_limits.per_user = 10
a těchto dvou pravidel v pg_limits.conf
all userA all 20 all userB all 5
platí že výchozí per-user limit je 10. Jedinými dvěma výjimkami jsou uživatelé "userA" který může otevřít 20 spojení a "userB" který může otevřít 5 spojení. Jediná podmínka aby k tomuto předefinováno došlo je že pravidlo musí být "pro uživatele" t.j. jediná zadaná hodnota musí musí být uživatelské jméno. A podobně pro databáze a IP adresy ...
Aktuální stav connection_limits
Pokud se chcete podívat na aktuální stav (které limity už jsou vyčerpány apod.), použijte pohled "connection_limits"
SELECT * FROM connection_limits;
Závěr
Tuto extension jsem začal psát před méně než týdnem, a zpočátku to byl spíš proof of concept. Nicméně většina problémů už je vyřešena a očekávám že ji brzy umístím na pgxn. Jedna z nevyřešených věcí je jak pracovat s hostnames v pravidlech. Aktuálně je hostname naparsováno ale kontrola není prováděna správně (pravidlo nikdy nebude vyhodnoceno jako odpovídající). Nejsem si jist jak toto správně řešit - nějaké nápady?




