Timeouty jsou jednoduchý, ale výkonný mechanismus, který může zabránit selhání systému a zajistit jeho spolehlivost. Často jsou však při vývoji softwaru opomíjeny, což vede k problémům, jako jsou visící procesy nebo vyčerpání zdrojů. Tento článek zkoumá důležitost správného nastavení timeoutů a poskytuje praktické rady, jak je efektivně implementovat. Zjistěte, proč jsou timeouty klíčové pro budování odolných a efektivních systémů.
Příběh
Jsem si jistý, že se vám to nikdy nestalo, ale zkuste si to představit. Váš on-call telefon právě obdržel zprávu: Web je dole! Otevřete svůj Nagios dashboard a přivítá vás moře červených varování. Všechny Apache servery nereagují. Databázové připojení je vyčerpáno. Load balancery hlásí, že nemají žádné zdravé backendy. Všechno ostatní je mrtvé ticho. Všichni začnou hádat, co se děje.
Konverzace a události popsané níže jsou zcela fiktivní a jakákoli podobnost se skutečnými osobami, živými nebo mrtvými, nebo skutečnými událostmi je čistě náhodná.
Team lead: Nasadil někdo něco?
Dev: Nasadili jsme stránku před čtyřmi hodinami. Je to asi databáze, viděl jsem nějaké varování, že je vyčerpán connection pool, zavolejte DBA!
DBA: Databáze je v pořádku. Jen je tu 10x více otevřených připojení než obvykle z aplikačních serverů, ale nic se neděje. Udělal jsem jednu chybu před dvěma lety a teď je to vždy databáze, kterou obviňujete. Možná je to síť?
Síťový admin: Kdyby to byla síť, nic by nefungovalo. Ale skoro žádný provoz není. To musí být něco v kódu. Nebo se load balancery zbláznily, říká se tu, že jsou offline?
Operations: Load balancery jsou offline, protože backend servery timeoutují, takže nemají kam posílat provoz. Jste si jistí, že jste nic nenasadili?
Manažer: Jsme pod útokem? Vsadím se, že je to DDoS. Někdo řekl, že je tu 10x více připojení než obvykle, to vypadá jako útok.
… 10 minut podobných konverzací uplyne rychle …
SRE: Asi to nic nebude, ale víte ten widget, který máme na produktové stránce? Ten, co ukazuje, kolik lidí se právě dívá na tuto stránku? Máme na to mikroservisu, že? No, Redis instance, která drží data pro to, má load 120. To asi není dobré, že? Ale určitě to nemůže být důvod, proč je celá stránka dole, že? Myslím, že widget je pěkný, ale není zásadní pro funkčnost stránky.
Dev: Jasně, když volání selže, prostě ho nezobrazíme a všechno funguje normálně. Plánovali jsme to tak. To nemůže být ono.
SRE: Víte, to je ta věc. Služba funguje, ale odpověď trvá 10 sekund. Ale vy máte určitě velmi krátké timeouty, když voláte službu na produktové stránce, že ano?
Dev: …
SRE: Že ano?!
Jak jsme to mohli přehlédnout?
Možná jste nic podobného nezažili, ale já ano. A víckrát, než bych chtěl přiznat. I když jsou timeouty známou technikou pro zvýšení odolnosti už od pradávna, jsou (nebo přesněji jejich nedostatek je) nejčastějším zdrojem výpadků v jakémkoli dostatečně složitém systému, který jsem viděl. Existuje několik důvodů pro to.
Šílené výchozí hodnoty. Většina síťových volání a primitiv má timeouty zabudované. Ale tyto timeouty mají výchozí hodnoty nastavené v minulém tisíciletí. Vezměme si curl, nejpopulárnější knihovnu používanou pro HTTP volání. Má nastavený connection timeout na 300 sekund (5 minut). A celkový timeout žádosti je dokonce 0, což znamená, že nikdy nevyprší. Musíte si to nastavit sami na něco rozumného. Stejná situace je s většinou timeoutů v TCP/IP stacku a v mnoha knihovnách. V praxi jsou výchozí hodnoty obvykle tak vysoké, že je to, jako by timeout vůbec neexistoval.
Neúplné mentální modely. Když uvažujeme o složitém systému, používáme mentální model. V dokonalém světě by každý měl stejný mentální model, který by přesně replikoval skutečnost. A měli bychom krásný aktuální Visio diagram takového systému vytištěný na každé zdi. Bohužel, nežijeme v dokonalém světě. Dokumentace je obvykle rozptýlená po wiki a README souborech v repozitáři, pokud vůbec existuje. Každý pracuje s neúplným mentálním modelem a záleží na senioritě, jak daleko je model vzdálen od reality. Většina lidí nebude znát všechny pohyblivé části systému, natož vědět, jak spolu interagují. V takové situaci se drží toho, co znají, a hledají problémy tam. V případě úplného kolapsu se problémy projevují všude a je těžké rozlišit příčinu od důsledku.
Těžké testování. Většina běžně používaných testovacích technik nepokrývá případy, kdy volání, které obvykle trvá 10 ms, najednou trvá 10 sekund. Je téměř nemožné to pokrýt unit testem a dokonce i integrační testy obvykle řeší buď volání, které projde normálně, nebo zcela selže. Ještě jsem neviděl statický analytický nástroj, který by vám řekl “Používáte curl volání, ale nenastavili jste timeout pro připojení” (byl bych více než rád, kdyby mě někdo opravil). Je třeba manuálně zkontrolovat všechna místa pro externí volání a ujistit se, že jsou nastavena rozumná timeouty.
Buďte agresivní
Timeouty nejsou lékem na všechno. Mají však dva hlavní přínosy. Pomáhají předcházet řetězovým selháním, jako v popsaném fiktivním incidentu. A umožňují vám činit rozhodnutí, když je stále čas situaci zachránit.
I když si lidé uvědomují a nastavují timeouty, často jsou příliš dlouhé a když dojde k problému, moc nepomáhají. Když se jich zeptám, proč je na volání, které obvykle trvá 100 ms, nastaven 5sekundový timeout, vysvětlí mi, že k tomu mají důvody. Například:
“Během údržbového okna v noci se databáze přetíží a volání trvá mnohem déle než obvykle. Když jsme měli timeout nižší, dostávali jsme upozornění v noci. Zvýšení timeoutu to vyřešilo.”
nebo
“Máme jednoho zákazníka, který má na svém wishlistu 1000 položek, a když snížíme timeout volání wishlistové služby na 100 ms, wishlist pro něj přestane fungovat.”
Chápete, co mám na mysli. Všechny tyto důvody jsou tak trochu validní, ale jednoduše ukazují špatný design nebo problémy s kapacitou.
Pokud se vaše databáze během údržby přetěžuje, děláte něco špatně. Možná můžete rozdělit údržbu na častější, ale menší dávky? Nebo přestaňte produkovat odpad, který je třeba uklízet.
A co ten zákazník… Už jste se ptali svých obchodníků, zda tento zákazník skutečně přináší zisk, nebo je to jen vaše konkurence, která pomocí wishlistu monitoruje vaše ceny? A i kdyby to byl legitimní ziskový zákazník, jste si jistí, že chce vidět všech 1000 produktů na wishlistu na jedné stránce? Pravděpodobně ne, tak zaveďte stránkování a omezte jednu žádost na 10 položek nebo něco podobného.
Lidé se často bojí malých selhání a v procesu jejich prevence obětují celkovou odolnost systému, což naopak umožňuje, aby se staly obrovské, celosystémové výpadky. Říkám: Buďte agresivní s timeouty. Pokud váš systém má výjimky, jako jsem zmínil výše, které vám brání v používání agresivních timeoutů, vypořádejte se s nimi. Častěji než ne, jsou to kanárci, kteří upozorňují na slabiny v designu vašich služeb. Pomáhají vám odhalit technický dluh.
Výkonnostní rozpočet
Ale jak nastavit rozumné timeouty v komplexním systému? Jedna vygenerovaná webová stránka může zahrnovat několik volání na jiné komponenty a služby systému. Ať už jde o databáze, cache, interní nebo externí API. Dokonce i přístup k souborům na file systému může být považován za volání externí služby, protože v dnešní době soubor často nesedí na lokálním serveru a je náchylný k problémům se sítí, stejně jako jakékoli jiné volání služby.
Pomůže, když použijete koncept výkonnostních rozpočtů. Řekněme, že si stanovíte interní cíl, že stránka musí být vykreslena za méně než 5 sekund, jinak se to považuje za selhání. Samozřejmě není vaším cílem trvat 5 sekund na generování každé stránky, ale je to absolutní limit, po kterém selžete. Může to být timeout upstream load balanceru nebo CDN například. To vám dává časový rozpočet, do kterého se musíte vejít, včetně selhání. Nyní můžete začít zvažovat, jaká externí volání děláte a rozdělit mezi ně svůj rozpočet.
Řekněme, že máte volání na službu, která je pro stránku naprosto nezbytná. Bez informací z této služby stránka nemůže fungovat. To znamená, že pravděpodobně budete chtít nastavit timeout pro tuto službu na takovou hodnotu, aby vám zbyl čas v rozpočtu na opakování volání jednou nebo dvakrát, pokud první selže nebo vyprší.
Pak můžete mít službu, která není tak nezbytná, jako ta, kterou jsme zmínili v našem úvodním dramatu. Je vám dokonce jedno, když 5% zobrazení stránky neobsahuje informace z této služby. V tomto případě můžete nastavit timeout na 95. percentil typické doby trvání volání (za předpokladu, že máte taková data, což je zásadní pro jakýkoli druh výkonnostního rozpočtování). Tímto způsobem ušetříte svůj výkonnostní rozpočet pro věci, které opravdu potřebujete.
Za předpokladu, že používáte caching pro nějaký typ volání služby, můžete udělat ještě jednu věc. Typický scénář je, že získáte nějakou hodnotu ze služby, jejíž výpočet je drahý, a na chvíli ji uložíte do nějaké formy cache. Může to být memcached, Redis, to je jedno. Nastavíte dobu, po kterou ji chcete ukládat, jako timeout nebo expiraci na klíči. Tímto způsobem nejprve nahlédnete do cache, pokud je tam, použijete hodnotu, pokud ne, zavoláte službu pro získání čerstvé hodnoty. Ale můžete být chytřejší. Můžete udělat expiraci součástí dat, která ukládáte do cache, a nastavit skutečnou expiraci klíče na delší dobu, řekněme 3x vaši požadovanou dobu cache. To vám umožní použít tato mírně zastaralá data, pokud volání služby selže nebo vyprší. Možná to nesplňuje váš standard čerstvosti, ale v závislosti na vašem případě použití je to lepší než úplné selhání. To je zvláště užitečné v případech, kdy data naprosto potřebujete a volání služby je drahé, takže ve svém rozpočtu nemáte čas na opakování, pokud vyprší timeout.
Pokud přidáte všechny rozpočty volání dohromady, měli byste se stále vejít do svého celkového rozpočtu. Nebo můžete jít trochu přes, samozřejmě. Víte, jako v reálném životě, kde doufáte, že se neuskuteční všechny plánované výdaje. Ale představuje to riziko a měli byste si ho být alespoň vědomi.
Individuální rozpočty volání služeb jsou také užitečné v rozhovorech s lidmi, jejichž služby voláte. Pokud jste jediným spotřebitelem služby, mohou to použít jako svůj vlastní top level rozpočet a proces opakovat.
Přijměte chaos
Takže víme, jak by věci měly být postaveny, ale jak skutečně zajistit, že tak opravdu jsou?
Pomáhá, když tyto designové vzory specificky zmíníte ve svých coding standards nebo vývojové příručce nebo něčem podobném, pokud to máte. Také pomáhá, pokud máte proces code review a specificky zmiňujete kontrolu správného používání timeoutů v guidelines pro review. Umístěte to hned vedle kontroly SQL injection vektorů a dalších podobných kritérií, která můžete mít.
To stále nebrání lidem zapomenout dát timeouty nebo je nastavit na příliš dlouhé. Můžeme však použít techniku z oblasti chaos engineering.
Chaos Engineering je disciplína experimentování na distribuovaném systému za účelem budování důvěry v jeho schopnost odolat turbulentním podmínkám v produkci.
Konkrétně náhodně zavedeme zpoždění do naší odpovědi. Je to vlastně opravdu jednoduché a triviální na implementaci. Jako správce služby stačí vložit sleep()
volání do své služby a vyvolat jej s předem nastavenou pravděpodobností náhodně. Řekněme, že vaše služba normálně trvá 100 ms na odpověď. Nyní, v 0,01 % případů (1 z 10 000) pauzujete na 5 sekund a tím způsobíte, že volání trvá 5100 ms. Doporučuji, abyste to udělali a pak čekali. Také teď by byl dobrý čas vytvořit dashboard, který plní počet takových událostí. Nebo možná pěkný histogram trvání volání, hmm?
Po chvíli se mohou někteří kolegové objevit u vašeho stolu, vysvětlovat, že se jim najednou začalo v dashboardech objevovat něco zvláštního a klást otázky. To je dobrá věc a můžete s nimi diskutovat o svých výkonnostních rozpočtech a jak mohou nastavit rozumné timeouty a politiku selhání.
Pokud se nikdo neukáže, pak opravdu musíte začít mít obavy. Buď nikdo vaši službu nepoužívá, což může být buď dobrá, nebo špatná zpráva, ale spíše si nejsou ani vědomi, že vaše služba někdy trvá tak dlouho na odpověď. V tu chvíli byste to měli být vy, kdo je navštíví a zahájí konverzaci. Můžete jim dokonce poslat odkaz na tento článek (mrk mrk).
Doporučuji mít pravděpodobnost selhání konfigurovatelnou pro každé prostředí. Začněte s jejím zavedením v testovacím prostředí. Zvyšte míru selhání na 10 % nebo více a zvyšte zpoždění, protože obvykle testovací prostředí nezískávají tolik provozu a lidé jsou zvyklí, že věci v testu nefungují hladce. Musíte opravdu selhat katastrofálně, aby si toho někdo všiml. Jakmile budete mít pocit, že jste objevili všechno, je čas postoupit svou hru a zavést selhání do produkce. Samozřejmě, chcete, aby se to stávalo mnohem méně často a ideálně pouze během pracovní doby, kdy jsou všichni čerství a připraveni reagovat. Oh, a prosím, řekněte lidem, že to děláte.
Závěr
Pokud jste nebyli přesvědčeni dříve, doufám, že nyní jste, že timeouty nejsou zlé a ve skutečnosti jsou naprosto nezbytné, pokud chcete budovat odolný systém. I když je koncept triviální, jeho správné použití není. A kontrola správného použití je ještě těžší. Nikdo to nezvládne napoprvé, ale pokud identifikujete alespoň polovinu míst, kde by měly být timeouty, a nasadíte je, zvýšíte odolnost svého systému. Odolnost není binární, je to kontinuální škála.
Dejte mi vědět v komentářích, zda pro vás byly tyto informace užitečné nebo zda jste našli lepší způsoby, jak se vypořádat s timeouty ve svých systémech.
Téma odolnosti systémů je mi blízké a pokud se chcete dozvědět více na toto téma, doporučuji vám přečíst mé předchozí články: Čtyři úrovně odolnosti v systémech a Implementace Circuit Breaker Pattern.
Comments