Leaking RAM, czyli jak wykryć wyciek pamięci w NR

Namówiony przez @szopen do popełnienia tego postu chciałem się z Wami podzielić “przygodą”, jak przytrafiła mi się ostatnio z moją instancją HA, w której zaczęło padać wszystko po kolei…

Ale od początku.
Zaczęło się od nieprawdopodobnych lagów w sieci zigbee, sięgających …kilkunastu minut.
Np. wchodzę do domu i zamykam drzwi uzbrojone w czujnik. Mimo, że zamknąłem za sobą drzwi i czujnik jest zwarty, to i tak przez co najmniej 20 minut leci z głośników komunikat “Otwarte drzwi, pacanie!”… Można było oszaleć… Tym bardziej, że sygnalizacja otwartych drzwi uniemożliwiała wykonanie innych automatyzacji, np. uzbrojenie alarmu, który uznał, ze dom nie jest zabezpieczony na tyle aby się uzbroić. Podobne lagi zaczęły pojawiać się także na innych urządzeniach. Irytacji nie było końca…
Doszedłem do wniosku, że rozsypała się sieć zigbee. Serwer raz po raz zaliczał śmierć kliniczną, która objawiała się zżeraniem RAM’u do 100%, wykorzystaniem SWAP’a do 100% i zajętością procka do 97%. Wszystko się gotowało… Jedyne, co mi pozostawało to twardy reset serwera… aż do kolejnej jego śmierci…
Na porcie 4357 zaczęły się też pojawiać jakieś dziwne komunikaty w stylu Observer NOT HEALTHY lub Observer NOT SUPPORTED. Byłem załamany, bo zacząłem zdawać sobie sprawę, że powoli tracę prawie rok swojej pracy nad deploymentem HA. Wszystko jak krew w piach!
W prawdzie kopie zapasowe robiły się regularnie do chmury Google, ale nie wiedziałem, czy są one na tyle zdrowe, by było z czego odtworzyć tę pracę, gdy zawiodą już wszystkie procedury naprawcze przewidziane dla HA.
Wykonałem wszelkie dostępne kroki ratunkowe z poziomu terminala typu su repair, nawet odważyłem się cofnąć wersję HAOS o dwa oczka, ale na nic się to nie zdało. Degradacja postępowała w tempie geometrycznym… i wkrótce pacjent umarł. Twardy reset bowiem także nie przywracał mu życia. Nie było z nim żadnego kontaktu poza wciąż poprawnymi odpowiedziami na zaczepki typu ping <adres_IP> -t, co świadczyło jedynie o tym, że host HA jest poprawnie wpięty do sieci LAN.
Złożyłem pacjenta “do grobu” i …przeprowadziłem jego reinkarnację zasiewając na jego dysku nowy HAOS wg tej instrukcji. Do portu USB wpiąłem świeżo dostarczony SkyConnect zamiast Conbee II. Zainstalowałem na powrót wszyskie dodatki (bez uruchomienia).

Na sczęście udało mi się wczytać mój backup, ściągnięty wcześniej z chmury na lokalny komputer ale jednocześnie powyłączałem wszystkie dodatki i integracje.

Sieć zigbee oparłem tym razem na integracji ZHA, pozostawiając świeżą instalację Z2M tylko jako opcję zapasową do przyszłej obsługi bardziej opornych urządzeń. Wiązało się z potrzebą ponownego sparowania wszystkich urządzeń z donglem SkyConnect (niestety migracja z Z2M nie powiodła się pomimo kilku prób, co ostatecznie przekonało mnie o możliwych przyczynach dramatycznych opóźnień w sieci zigbee, o kórych pisałem tutaj.
Zrobiłem research NR, którego @szopen słusznie podejrzewał o leaking pamięci i zacząłem metodycznie analizować przepływ za przepływem. Plan pracy był taki:

  • Wyłączyłem wszystkie aktywne zakładki na dashboardzie NR
  • Zacząłem deployować każdą z nich osobno zaczynając od najprostszych, sprawdzając przy tym pilnie jak jej uruchomienie wpływa na zasoby serwera
  • Po każdym udanym uruchomieniu danej zakładki (założyłem, że uruchomienie będzie udane wtedy, gdy zasoby sprzętowe nie zostaną pożarte przez uruchomione przepływy) robiłem kolejną kopię zapasową.
  • Kroki powtarzałem dla każdej zakładki osobno, aż wreszcie jedna z nich faktycznie ujawniła swoje niszczycielskie oblicze niczym pirania, zżerając RAM do zera, co po kilku minutach spowodowało także, że SWAP wyniósł 100%, po czym serwer kolejny raz całkowicie odmówił współpracy.
  • Po zidentyfikowaniu trefnej zakładki (i wykonaniu restore z najświeższej kopii sprzed chwili) wyłączyłem ją i zacząłem przenosić poszczególne logiczne jej składniki na zakładkę roboczą. Każdy taki ruch kończył się deploymentem tylko zakładki roboczej i natychmiastową analizą dostępności zasobów. Wciąż nie zapominałem o robieniu kopii zapasowych…
    W ten sposób ujawniłem cztery nody, których deployment czynił niezdatnym do dalszej pracy całe środowisko HA (zżarty RAM = 100%, SWAP` = 100%, zajętość CPU = 97% i w efekcie końcowym “śmierć kliniczna” serwera).

Te 4 nody wyglądają niewinnie:

[{"id":"1287ec357b2fad2c","type":"poll-state","z":"b1a59f7a37c16498","name":"Temp. pieca","server":"869cd94c.d8cc48","version":2,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"updateinterval":"","updateIntervalType":"num","updateIntervalUnits":"minutes","outputinitially":false,"outputonchanged":true,"entity_id":"sensor.control_cc1_flowtemperaturesensor_21222100209650938031177n9","state_type":"str","halt_if":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"x":775.7142944335938,"y":150,"wires":[[]]},{"id":"d89841c5f1efa4bc","type":"poll-state","z":"b1a59f7a37c16498","name":"Bojler tryb","server":"869cd94c.d8cc48","version":2,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"updateinterval":"","updateIntervalType":"num","updateIntervalUnits":"minutes","outputinitially":false,"outputonchanged":true,"entity_id":"sensor.tryb_pracy_bojlera","state_type":"str","halt_if":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"x":445.71429443359375,"y":150,"wires":[[]]},{"id":"15741b4a2808e8f5","type":"poll-state","z":"b1a59f7a37c16498","name":"Piec tryb","server":"869cd94c.d8cc48","version":2,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"updateinterval":"0","updateIntervalType":"num","updateIntervalUnits":"minutes","outputinitially":false,"outputonchanged":true,"entity_id":"sensor.tryb_pieca","state_type":"str","halt_if":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"x":445.71429443359375,"y":200,"wires":[[]]},{"id":"7c1333baab7f663d","type":"poll-state","z":"b1a59f7a37c16498","name":"Temp. bojlera","server":"869cd94c.d8cc48","version":2,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"updateinterval":"0","updateIntervalType":"num","updateIntervalUnits":"minutes","outputinitially":false,"outputonchanged":true,"entity_id":"sensor.control_dhw_domestichotwatertanktemperature_212221002026096538031177n9","state_type":"str","halt_if":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"x":775.7142944335938,"y":200,"wires":[[]]},{"id":"869cd94c.d8cc48","type":"server","name":"Home Assistant","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":"30","areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true}]

Cały problem wziął się stąd, że nie określiłem zawczasu częstotliwości odczytu 4 sensorów w nodach pool state błędnie zdefiniowanych w pliku sensors.yaml, przez co encje te w ogóle nie istniały. NR przyjął więc najmniejszą jednostkę domyślną ich odczytu: 1ms. Efekt był oczywisty: każdy z tych nodów odpytywał o nieistniejącą wartość swojego sensora aż 1000 razy na sekundę. Może komputer kwantowy dałby sobie z tym radę, ale zwykły NUC po prostu wymiękł… I wcale mu się nie dziwię, też bym wymiękł :wink:
Nawiasem mówiąc szkoda, że nie ma w NR jakiegoś bardziej zaawansowanego mechanizmu walidacji danych już na etapie ich wprowadzania, pewnie szybko wyłapałbym swój błąd i zaoszczędziłbym sporo czasu, stresu i roboty.

Te ‘trefne’ sensory to tak na prawdę atrybuty encji climate.piec_vaillant, które zamieniłem na odrębne encje (sensory), a które miały mi w prosty sposób pokazywać czy bojler/piec:

  • są załączone
  • czy obieg jest podgrzewany
  • czy pompa pracuje
  • jaka jest temperatura pieca a jaka bojlera
  • w jakim programie (predefiniowanym przez producenta: home, away, day, night, party, auto, off) urządzenia pracują w danej chwli - u mnie piec i bojler mogą pracować w róznych programach jednocześnie, niezależnie od siebie…
    Nie jestem jeszcze zbyt biegły w płynnym korzystaniu ze składni językowej JSON czy Javascript, dlatego łatwiej mi było operować wartościami encji (po to je stworzyłem, niestety z błędami), niż ich atrybutami…
    A na dodatek integracja Vaillant (Multimatic) jest wyjątkowo trudna do opanowania i konfiguracji, nie wpomnę o kiepskiej i nieintuicyjnej dokumentacji…

Po całej akcji naprawczej teraz mam tak:
image

image

Więc chyba mogę odtrąbić sukces, czego i Wam życzę w podobnych sytuacjach :slight_smile:

6 Likes

@Krzysztonek ciekawa lektura do porannej :coffee:.
Jak wrażenia po przejściu z Z2M na ZHA? Zrobiłeś to tylko dlatego, że kupiłeś SkyConnect?

Niestety, wrażenia generalnie słabsze od oczekiwanych… ZHA rozpoznał wszystkie moje switche (np. w gniazdach el.) jako urządzenia z domeny light. Trochę głupio to wygląda, gdy np. elektryczne ogrzewanie podłogi staje się teraz ‘światłem’ :smiley: Poza tym zauważyłem, że:

  1. to samo urządzenie zgłasza inne sensory w ZHA a inne w Z2M. Np. czujnik PIR ma teraz 2 sensory: _ias_zone oraz znany z Z2M sensor _occupancy. Trzeba Ci wiedzieć, że _occupancy to sensor fejkowy. Jest teraz niczym innym jak sensorem _ias_zone ale z 10-minutowym timerem. Trochę to bez sensu…
  2. generyczne nazwy własne sensorów generowane przez ZHA są trochę ‘z czapki’ (np. głowica grzejnikowa zgłosiła 3 sensory typu ‘switch’, choć żadnego nie posiada. Klinięcie w taki switch niczego nie uruchamia ani nie wyłącza…
  3. jeśli zmienię nazwę urządzenia (i jego encji) to nie idzie za tym automatyczna zmiana pozostałych encji powiązanych, jak ma to miejsce w Z2M.
  4. problem z niezgodnością klas/domen pociągnął za sobą konieczność odpowiedniej adaptacji nodów w NR (głównie nody call service).

Bezsprzecznym plusem jest szybkie parowanie i łatwiejsze, intuicyjne zarządzanie samym urądzeniem przez konsolkę, umożliwiającą dostęp do atrybutów klastrowych oraz poleceń wybranego klastra.

Nie. Z powodu znacznej ilości urządzeń (aktualnie ponad 140) postanowiłem zrównoważyć całą sieć zigbee tworząc jej 2 ‘instancje’ (istniała obawa, że jedno radio będzie miało za małe zasoby by uciągnąć tyle urządzeń): jedną obsługuje ZHA + SkyConnect (w niej znajdują się urządzenia typowe: czujniki PIR, termometry, czujniki okien, wyłączniki świateł) a drugą obsługuje Z2M + Conbee II (tutaj siedzą urządzenia bardziej problematyczne, np. głowice grzejnikowe, sterowniki rolet, automaty do bram, sensory falowników z instalacji PV). Sieci ZHA i Z2M pracują na dwóch różnych kanałach aby zminimalizować poziom wzajemnych zakłoceń i interferencji i aby całość nie była “gwiazdą śmierci”, jak słusznie zauważył @artpc .

1 Like

Taki mały offtopic - można mieć kilka instancji Dodatku Z2M, więc generalnie nie ma musu używania jednego koordynatora z Z2M, a drugiego w ZHA.
Sztuczkę opisywał kiedyś @rafkan - sprawdzone - to działa nawet z 3 niezależnymi instancjami Dodatku

A drugie zagadnienie - projekt ZHA jest całkowicie odrębny od Z2M, stąd zupełnie inne rozwiązania i istotnie wiele rzeczy rozwiązano zupełnie inaczej, więc zmiana jednego na drugi nie jest ani oczywista ani łatwa.

Do ZHA jest dostępny też taki zestaw narzędzi (komponent niestandardowy)

Czy dla Z2M działa to także w drugą stronę? Tzn. czy można mieć zainstalowanych kilka instancji logicznych Z2M z jednym i tym samym fizycznym kordynatorem?
Chyba nie, skoro każda instancja Z2M ma zdefiniowany inny kanał zigbee… Ale może koordynator umie ogarniać kilka kanałów jednocześnie…

W jaką drugą stronę? Jeden koordynator = jedna integracja (tak jest zawsze).

Dwa koordynatory = 2 integracje, a że Z2M daje się zainstalować w kilku instancjach to można mieć kilka równolegle pracujących sieci Zigbee w jednej instalacji (można też “combo” ZHA z jednym koordynatorem, a Z2M z innym, ale ZHA nie może wystąpić 2x a przynajmniej tak było gdy ostatnio to sprawdzałem).

Sieć Zigbee konstrukcyjnie działa tylko na jednym kanale (z wielu do wyboru, choć “jedyne słuszne” są tylko cztery, które umożliwiają pracę też osprzętu ZGP).

Tak też podejrzewałem, ale wolałem się upewnić, bo sam proces instalacji Z2M teoretycznie umożliwia utworzenie wielu konfiguracji (każda z nich w innym folderze), w tym np. wskazanie różnych kanałów dla tego samego fizycznego koordynatora.

Tak umożliwia, ale zwariujesz jak będziesz musiał zarządzać ich backupami (tzn. jestem przekonany, że to się źle skończy).
Jak masz mus eksperymetów to kurzy się u mnie jakieś CC2531