Programio

Čtyři nejtrapnější chyby, jaké jsme během vývoje udělali

Trvalo nám asi čtvrt roku, než jsme rozběhli celý systém založený na Kafce, Samze a Druidu několik měsíců potom jsme ještě lovili a řešili další vzniklé bugy. Cestou jsme samozřejmě narazili na hromadu chyb a některé z nich byly tak blbé, že si měl člověk chuť vystřeli mozek z hlavy po jejich vyřešení. Obyčejně to byly chyby, které si chcete nechat pro sebe a rozhodně se s nimi nikde nechcete chlubit, nedej bože někde na veřejnosti. Tak přesně o těchto chybách bude tento článek.

Jak jsme konfigurovali Druida

Nejblbější chyba jakou jsme kdy udělali ― chtěli jsme nastavit, aby druid.announcer.type bylo typu batch. Zní to dost jednoduše, takže jsme editovali soubor a vložili jsme tam příslušný řádek:

druid.announcer.type=batch 

Restartovali jsme Druida a zdálo se, že to funguje. Po pár hodinách už to ale tak nevypadalo a Druid se choval divně. Googlíme příznaky a většina odpovědí se shoduje, že tyto příznaky jsou obvykle způsoby tím, že config druid.announcer.type není nastavený na batch. Kokuneme do configu a vidíme, že je. Po pár dalších zoufalých hodinách už zkoušíme přes ctrl+c a ctrl+f, jestli je to fakt do písmene stejné a ano, je.

Procházíme logy všech pěti Druidích nodů, koukáme se do ZooKeeper logu, hledáme, jesti chyba není v MySQL, kterou Druid používá. Co HDFS, nenaříká nějak? Ani ne, běží jako Forrest Gump. Jdeme na GitHub a koukáme přímo do zdrojáků Druidu, jestli tam něco nevyčteme. Vyčetli jsme, že by to mělo fungovat, když máme druid.announcer.type nastavený na batch.

Bystřejší, zkušenější a sečtělejší ví. Mezera na konci. Debilní mezera navíc na konci řádku za slovem batch, se kterou si aplikace neporadila a neodstranila ji. Změnili jsme config na

druid.announcer.type=batch

a najednou vše fungovalo jak mělo. Kravský .properties formát.

Jak jsme konfigurovali Cassandru

Používáme Cassandru a jejího Node.JS klienta. V minulosti jsme narazili na docela dost výkonnostních problémů s touto kombinací, takže jsme vynaložili nemalé úsilí, abychom se dostali na nějaké rozumná čísla. Problémů jsme měli víc, některé klasické, sem tam byly chyby v kódu … a pak tam byla jedna zábavná chyba.

Abyste rozuměli, když vytváříte nového Cassandra klienta, děláte to cca takto:

1
2
3
4
5
var client = new Client({
queryOptions: {
consistency: types.consistencies.quorum
}
});

Třídě Client předáte konfigurační objekt, jednoduché, prostě a snadno použitelné. Nakonfigurovali jsme klienta jak jsme potřebovali, klient se rozběhl a fungoval.

Až za čas jsme jaksi zjistili, že některé z těch configů, které jsme tam vrazili, jsou až ve vyšší verzi Cassandra driveru (= měli jsme 2.1.1 a vyžadovala se 2.1.2) a klient se nám neobtěžoval sdělit, že používáme nevalidní config (kluci prostě zmergili default config a user config a nazdar bazar). Ve výsledku jsme si tak několik týdnů mysleli, že máme driver nějak nakonfigurovaný a driver samotný danou konfiguraci vlastně vůbec nepodporoval. Tak jsme si updatli knihovnu a svět byl zase růžovější.

Takže vás pěkně prosím: validujte configy! (My je samzořejmě také nevalidujeme, lol.)

Jak jsme neodesílali zprávy do Kafky

To si takhle rozjedete Node.JS appku, která má něco zapisovat do Kafky, nastartujete ji a chystáte se do té Kafky něco odeslat. Jenže ouha, protože je to článek o našich chybách, tak to samozřejmě nejde. Dostáváte jedinou smysluplnou hlášku, něco jako “Could not send a message, no Kafka broker available.”

Co teď? Prozkoumáme Kafku, jestli běží, koukneme do logu, všechno se zdá v pohodě. Cvičně se zkusíme připojit z daného stroje na Kafku telnetem, to jde. Stejně tak jsme schopni spustit konzolového producera a poslat do Kafky nějakou zprávu. OK, takže se serverem je všechno v pořádku, žádný firewall nám neutíná spojení, chyba musí být přímo v Node.JS aplikaci.

Střídavě spouštíme appku v debug módu a střídavě přidáváme různé logy do výpisu. Jsme docela rádi, že máme u sebe celý zdrojový kód, včetně všechn knihoven a že jsme schopni ho měnit. A co nevidíme. Nedaří se přeložit doména zookeeper01.

My když jsme totiž zkoušeli toho konzolového producera, tak jsme ho spouštěli cca takovým příkazem:

bin/kafka-console-producer.sh --broker-list kafka01:9092 --topic test

Jenomže naše knihovna potřebovala pro připojení Zookeeper, takže se napřed připojovala na Zookeeper, ale to se jí nepovedlo, protože neuměla přeložit doménu zookeeper01, neboť jsme ji zapomněli přidat do /etc/hosts. A ta kravská knihovna nám ani nikde nedala v logu vědět, v čem je vlastně problém.

Tady už jsme se skoro odhodlali prohrábnout se kódem knihovny a napsat pull request s opravou, ale kód byl tak složitý/nesrozumitelný, že jsme se na to vykašlali a na kontrolu domén jsme si napsali skript bokem.

Jak jsme nastavovali Samzu

Měli jsme takový zábavný problém se Samzou. Její úkol byl číst zprávy z Kafky, zpracovat je a poslat dále do Kafky. Vše fungovalo správně jak mělo, ale když jsme zkoušeli nějaké performance testy, tak se nám stávalo, že nám Samza při větší záteži ztrácela nějaká data. Po chvíli zkoumání jsme přišli na to, že je Samza neztrácí, že jen zkrátka čte tak polovinu zpráv a druhou polovinu nějak vynechává.

Začali jsme zkoumat, jestli náhodou nečteme jenom některé partitony. V Kafce máme standardně 24 partitionů, kterými tečou data, tak jestli se třeba nestalo, že čteme jen polovinu z nich. Tím to samozřejmě nebylo. Ještě chvíli zkoumáme Samzu a docházíme k závěru, že chyba taky může být v Kafce.

Vedle Samzy si ještě pustíme Konzolového konsumera a zkoušíme číst ta samá data, která čte Samza, a ukládáme je do souboru. V souboru jsou všechny zprávy, přesto Samza všechny zprávy nepřečetla. V Kafce tak chyba být nemůže, tam jsou všechna data, zpět do Samzy.

Přidáváme logy do Samza jobu, s každou přečtenou zprávou už logujeme tak deset řádků. Po pár minutách běhu Samzy mají logy klidně deset gigabajtů. Tady přestává stačit i grep; uvažujeme, že na jejich analýzu nasadíme Excel.

Pak se to někdy stane. Někdo vysloví spásnou myšlenku: “jako by to Kafka Samze mazala přímo pod rukama”. Hehe. Taky že jo - během nastavování Kafky o dva týdny dříve jsme měnili retenci, tj. jak dlouho si má Kafka nechat uložené zprávy na disku. To nastavení vypadalo přibližně takto:

log.retention.bytes=100000000

Ano, z nějakého mně neznámého důvodu se retence udává v bajtech. Nemůžete danou hodnotu zadat v nějaké rozumné jednotce, musíte ji zadat v bajtech. No a když si nedáte pozor, tak se může stát, že místo 100 GB nastavíte 100 MB. Co se tedy stalo? Kafka každou chvíli tikla, zjistila, že má na disku třeba 1 GB zpráv, přitom má nastavený limit na 100 MB a 90 % zpráv, které tam měla, smazala. Proto Samza dostávala jen část dat - zbytek už jí Kafka nemohla dát.

Konzolový konzumer měl vždy všechna data čistě proto, že zprávy četl mnohem rychleji než Samza a stihl je tak přečíst ještě předtím, než je Kafka stihla smazat.

Závěr

Pokud něco nefungovalo, měli jsme nejčastěji chybu v konfiguraci a často to byla dost trapná chyba. V minimumálním množství případů za to mohl náš kód. Z toho nám vyšlo, že bychom měli nějakým způsobem testovat configy.