Je třeba zabít PostgreSQL
Čas od času je potřeba během vývoje otestovat reakci aplikací na "úmrtí" databázového serveru. V PostgreSQL mailing listu se na toto téma odehrála poměrně zajímavá diskuse, ve které se objevilo několik zajímavých přístupů (od velmi elegantních až po brutální). Podívejme se tedy na jednotlivé způsoby zabíjení PostgreSQL ...
Je poněkud naivní při vývoji aplikace vycházet že databáze bude 100% dostupná. Ačkoliv můžeme věřit že zrovna právě implementace naší databáze je bezchybná a nikdy "nespadne," existuje spousta dalších možných příčin nedostupnosti databáze, mimo jiné zejména:
- pád PostgreSQL procesu - chyba v implementaci databázového serveru, chyba nebo volání abort() v C proceduře, ...
- administrátorský zásah - shození PostgreSQL serveru, reboot systému, ...
- problémy operačního systému - problémy se zdroji (nedostatek file descriptorů, atd.)
- hw problémy - omezená schopnost reakce v důsledku přetížení I/O susbystému, ...
- síťové problémy - přerušení spojení, vysoká latence, ...
Důležité je že různé příčiny mají z pohledu aplikace různé projevy - v některých případech aplikace dostane informaci o ukončení spojení, někdy prostě čeká a čeká, někdy server odpovídá velmi zpomaleně, atd.
Vyvíjíte-li důležitou aplikaci u které je žádoucí maximální dostupnost, není dobré spoléhat se na bezchybnost databáze, sítě nebo hardware. Naopak je žádoucí aby aplikace na nedostupnost databáze správně reagovala (např. nepřetěžovala síť či databázi a nepůsobila tak více škody než užitku), a dokázala se pokud možno sama zotavit.
Podívejme se tedy na způsoby jak tyto různé příčiny nedostupnosti nasimulovat ...
Pád PostgreSQL procesu
První možností je simulace pádu PostgreSQL backendu, obsluhujícího dané spojení. To lze simulovat několika způsoby - například Craig Ringer navrhl možnost je zavolání C procedury, která buď zavolá funkci abort(), nebo která záměrně provede chybnou operaci (např. dereferencování null pointeru).
Dále je samozřejmě možné zabít příslušný proces na úrovni operačního systému - například voláním dobře známého příkazu kill
kill -9 PID
kde PID reprezentuje ID procesu daného backendu (pochopitelně můžete použít i jiné signály, ne jen 9 tj. KILL - například SEGV je poměrně zajímavá možnost).
Další možností je volání příkazu pg_ctl
pg_ctl -m immediate stop
který "natvrdo" shodí daný PostgreSQL cluster.
Nenechte se zmást dojmem že není rozdíl mezi pádem PostgreSQL backendu a pádem celého serveru - není to pravda. Podrobnosti najdete v příspěvku Craiga Ringera, nicméně dovolím si alespoň stručné shrnutí.
Pád PostgreSQL backendu
V případě pádu PostgreSQL backendu je klient relativně dobře informován. Pokud má klient otevřené spojení na backend který spadne a pošle paket, dostane zpět odpověď TCP RST signalizující že spojení bylo přerušeno, případně mu OS v okamžiku pádu backendu zašle TCP FIN, tj. signál že se spojení uzavírá.
Pokud se klient pokusí otevřít nové spojení na server na kterém PostgreSQL neběží, dostane odpověď TCP RST, tj. opět aktivní odmítnutí spojení.
Na druhou stranu pokud backend běží ale je nedostupný (např. deadlock), potom klient na svoje pakety dostane ACK (potvrzení o přijetí), ale dál se nebude nic dít - alespoň do okamžiku kdy se zaplní buffery. Z pohledu TCP stacku na klientovi se nic zvláštního neděje - packety jsou úspěšně doručovány, a to že se s nimi nic neděje není problém na úrovni TCP.
Nedostupnost celého serveru
V případě nedostupnosti celého serveru je situace daleko komplikovanější, a v jistém smyslu také horší, protože klient na své pakety nemusí dostat vůbec žádnou odpověď. Server dokonce může přestat odpovídat na ARP dotazy - v tom případě může (ale nemusí) nejbližší router klientovi vrátit ICMP odpověď "destination unreachable." Důsledkem jsou dlouhé prodlevy (timeouty) na straně klienta, než TCP stack usoudí že spojení "umřelo," a klient bude čekat na volání funkcí jako jsou recv(), read() a podobně.
Pochybuji že byste chtěli takto brutálně zabíjet celý server (byť jenom vývojový), ale toto chování můžete poměrně dobře simulovat na úrovni sítě - viz. dále.
Problémy operačního systému
Co se týká problémů na úrovni operačního systému, zaměřme se na problémy se zdroji (paměť, otevřené soubory, ...), resp. s limity které operační systémy na tyto zdroje kladou. V případě dosažení stanoveného limitu musí operační systém nějak reagovat (zabít vybraný proces aby uvolnil paměť, znemožnit otevření dalšího souboru, apod.).
Jak poznamenal Vick Khera, jedním z takových limitů je například počet otevřených souborů - ať už celkový nebo "na proces." Dosažení limitu na počet otevřených souborů "na proces" (nastavení max_files_per_process v postgresql.conf) není obvykle příliš pravděpodobné. Daleko pravděpodobnější je dosažení limitu na celkový počet otevřených souborů, a simulace dosažení tohoto limitu není příliš obtížná - zjistěte si kolik souborů potřebuje PostgreSQL proces, snižte limit tak aby při otevření spojení a nastartování procesu došlo k dosažení tohoto limitu.
Obecně se doporučuje nastavit maximální počet PostgreSQL backendů (max_connections) tak aby po vynásobení maximálním počtem otevřených souborů "na proces" (max_files_per_process) nebyl překročen systémový limit na počet file descriptorů.
Hardwarové problémy
Hardwarové problémy - selhání napájení, chyby paměti a disků - mají většinou za následek pád celého serveru. Existují ale i hardwarové problémy které mohou způsobit například přetížení I/O subsystému (problémy s diskovými řadiči a poli, apod.) a následně i sníženou schopnost reakce celého serveru.
Jednu z možností jak toto chování simulovat pomocí signálu SIGSTOP nastínil Craig Ringer.
Síťové problémy
Pravděpodobnost problémů se sití (vysoké latence, přerušování spojení, vysoká ztrátovost paketů apod.) závisí výrazně na architektuře vaší sítě. Máte-li dva servery umístěné v serverovně hned vedle sebe, potom asi mnoho problémů se sítí nelze očekávat.
Naopak máte-li jeden PostgreSQL server ke kterému se připojuje více uživatelé (využívající např. desktopové aplikace) z celé firmy nebo dokonce vzdáleně (z domova přes dial-up apod.), lze problémy se sítí očekávat a aplikace by na ně měla být schopna zareagovat.
Dva jednoduché a přímočaré způsoby jak simulovat nedostupnost, spočívající ve shození resp. zablokování síťového rozhraní, navrhli Greg Sabino Mullane a Ray Stell
# shození rozhraní (Ray Stell) $ ifconfig ethx down # zablokování rozhraní (Greg Sabino Mullane) $ iptables -I INPUT -p tcp --dport 5432 -j DROP
Potřebujete-li simulovat složitější síťové problémy, můžete využít projekt LARC (Linux Advanced Routing & Traffic Control) doporučovaný Craigem Ringerem, či Net:Netem doporučovaný opět Craigem Ringerem.
Jak již bylo uvedeno výše, na síťové úrovni lze simulovat i pád celého serveru - stačí prostě požírat všechny pakety. Dále myslím není třeba se v tom pitvat.
Administrátorský zásah
Nikdo není neomylný, administrátory nevyjímaje - čas od času se prostě stane že administrátor udělá chybu. Například zadá "sudo reboot" v konzoli směřující na produkční PostgreSQL server, omylem zastaví PortgreSQL cluster, a podobně. Nedostupnost však může způsobit i korektní zásah administrátora, nemusí se nutně jednat o chybu - například v případě upgrade apod.
Nicméně simulace těchto omylů není obtížná - buď přímo nebo s využitím předchozích částí.




