NEWS
[Kolumne] Ein bisschen Speed muss sein
-
Hi,
da bisweilen die unbekannten Weiten des ioBroker-Universums locken und bei uns gerade Europas größtes Samba-Festival beginnt (schön [laut], aber nichts für mich), habe ich mir heute Abend als Zwischenziel gesetzt, herauszubekommen, wieviele Events ioBroker auf meiner Maschine pro Sekunde so abarbeiten kann.
Ich habe da Dinge in Posts gelesen wie
… habe 60.000 Objekte, alles ist gut
... 200 Objekte ist schon viel
... meine History geht nicht, alle 10 Minuten ist da Neustart
... 200 Events und du kannst den Admin dichtmachen
... habs auf 1000 eingestellt
... da must du auf redis umstellen
... In-Memory ist super
... Out of memory-Fehler im js-controller
Für jeden ist also etwas da.
Was ich nicht gefunden habe ist die Aussage: "Mein ioBroker-Server ist so langsam, dass er Ereignisse <u>verschluckt</u>."
Kein Datenverlust. Bei so vielen verschiedenen Installationen auf nahezu ebenso vielen verschiedenen Plattformen. Das ist doch eine Ansage. Das kann nicht jeder!
Fürs erste Mal behaupte ich einfach, es ist relativ egal, wieviele Objekte man im System definiert hat. Wenn da nichts passiert, ist da auch nichts unter Last. Für die Performance ist interessant, wieviele Objektänderungen mein System verträgt. Und ob genügent Zeit für die sich daran anschließenden Aktionen verbleibt. Wenn gepuffert wird, ob der Speicher reicht.
Auch klar sein sollte, dass ein Raspberry Pi eine andere Größenordnung als der Multicore HP DL380Gen10-Server darstellen wird.
ioBroker ist ausgesprochen Event-orientiert aufgebaut. Man kippt oben ein Ereignis rein -Taster gedrückt - und es macht einfach klickediklack. Anschließend fallen unten jede Menge Events heraus, die das Ablegen des Wertes in der History-Datenbank veranlasssen, unter Umständen mehrere Java-Scripts triggern, in VIS die Visualisierungen kippen lassen und am Ende noch Alexa zum Singen bringen.
Mein Maschinchen ist ein typischer in die Jahre gekommener WIndows 7 auf 10-Rechner. Intel Core i7-2600 CPU, 16GB RAM. Man munkelt, bei der Primzahlberechnung wäre er etwa 13-mal schneller als ein Raspberry Pi. (http://www.roylongbottom.org.uk/Raspber … hmarks.htm) Allerdings habe ich ihm eine SSD gegönnt. Das schlägt jede über USB angebundene SD-Karte. Was aber für den ersten Test egal ist, man wird sehen.
Zur Zeit sitze ich daran, einen kleinen Adapter zu schreiben. Als Nebeneffekt kippt er von außerhalb Events in das ioBroker-Ökosystem.
Im ersten Test setzt jedes von außen eingehende Datentelegramm jedes mal den State eines festen Objekts, nennen wir es "adapter.0.testVariable". Der State ist vom Datentyp JSON, also Text. Damit wir etwas von den Änderungen des Wertes haben, wird im JS-Adapter eine Subscription darauf eingerichtet. Die Persistierung, d.h. die Speicherung des States in der Historie ist vorerst ausgeschaltet. Dafür machen wir im Script ein bisschen Filterung. Man beachte den "performanten" Volltextsucheansatz in der Payload der Message mittels .search():
on({id: 'adapter.0.testVariable', change: "ne"}, function (obj) { try { msg = JSON.parse(getState(obj).val); } catch (e) { console.error(e.message + ' --- Fehler beim JSON-parsen: ' + getState(obj.val)); return; } if (msg.level == 'notice' && msg.message.search("ort") > -1) { setState('javascript.0.testAlarm',true,true); log('testAlarm ist true'); } });
Nachdem der externe Event-Generator angeworfen wurde, pendelt sich im Instanz-Fenster des Admins zügig die folgende Anzeige ein:
Out of the Box 60.000 Events alle 15 Sekunden abgeliefert aus dem Netzwerk und übegeben an den ioBroker. Das ist nicht von schlechten Eltern. Würde ioBroker keine Freeware sein, wäre das ein prima Vermarktungsargument. Denn dafür steht die Zahl 60004 in der Spalte Ereignisse beim unteren Adapter. Für diejenigen, die gerne den Datendurchsatz berechnen wollen: Jedes Event ist ohne Objekt-Header 107 Byte groß.
Ein Eingangsevent kann z.B. von der Änderung eines States beim Anlegen oder dem Setzen eines Wertes ausgelöst werden. Ausgangs-Events umfassen Aktionen wie Wertevergleich, schreiben eines Wertes in die States-DB, Events aufgrund von Subscriptions oder das Logging eines Adapters um z.B. Aussagen über .connected- oder .alive-States zu bekommen. So kommen auch die 8 Events zustande, die typischerweise beim Ereignisausgangswert in der Instanzen-Liste stehen.
… 60.000
Wie steht es um die CPU und dem Speicher unter dieser Last? Betrachten wir zuerst den js-Controller. Er verteilt die Events und persistiert Objekte und ihre States in den zugehörigen Datenbanken:
Im Schnellcheck: 12,5% CPU konstant dazu keinen Speicherzuwachs, also keine Warteschlangen. Der IO ist vernachlässigbar, da immer nur (fast) ein Wert geschreiben werden muss. Sieht doch erst einmal gut aus, oder? Nein, ist es nicht. Denn ein node-Prozess ist single-threated. 12,5% CPU-Nutzung sind 100% Nutzung eines einzelnen Cores. Berechung: 100% Gesamtleistung /8 Cores hier = 12,5%
Zur Nutzung der heutigen Multicoresysteme mit node braucht man Techniken wir Child-Prozesse oder unabhängie Workerthreads. Beim ioBroker haben wir individuelle Adapter. Diese laufen jeweils als einzelne Node-Prozesse, verteilen sich auf die verschiedenen Cores und tauschen zur Kommunikation Events miteinander aus. Mehr zum Thema unter http://rickgaribay.net/archive/2012/01/ … aded.aspx
Der js-controler kann und wird also unter <u>Hochlastbedingungen</u> im aktuellen Systemdesign einen absoluten Flaschenhals darstellen. Da helfen auch nicht mehr Cores, eher eine schnellere CPU. Das gilt aber auch für andere Adapter, die viele Ereignisse abonieren, wie z.B dem Javascript-Adapter. Bei Multihost-Systemen verteilen sich zwar die Adapter, aber wir haben trotzdem als limitierendes Element einen zentralen Master-js-Controller-Prozess. Dieser kann unabhängig davon wieder mit redis entlastet werden, einem eigenständigen Server für Datenstrukturen.
Der Javascript Adapter bekommt über 84.000 Events innerhalb von 15 Sekunden angeboten. Ein Gutteil dieser Ereignisse triggern das Filterscript mit der unperformaneten Volltextsuche. Ergebnis: Konstanter Speicher, etwa 8-9% CPU und Erwartungsgemäß kein IO. Keine Warteschlangen.
Der Quell-Adapter übrigens, der sich um Netzwerkkommunikation und Objektübersetzung kümmert, benötigt in diesem Szenarion etwa 6% CPU. Macht insgesamt mit den anderen node-Prozessen ca 30% CPU-Auslastung. Da kann man fast noch Videoschnitt nebenher machen; es sInd ja noch Cores frei…
Etwas anders sieht es aus, wenn die Eventgröße von ca. 107 Bytes auf über 4KB zunimmt. Jetzt stell das Javascript und dort vor allem dort die Suche ein emminentes Hindernis dar. Der JS-Adapter läuft weiterhin mit etwa 10-11% CPU. Auch der js-controller steck bei 12,5% CPU fest. Beide laufen jetzt also am Limit.
Erwarungsgemäß ist die Anzahl der verabeiteten Events gesunken. Das liegt aber auch am verwendeten Eventgenerator und am Netzwerk. Auch gigabit kommt an seine Grenzen. Grenzen gibt es jetzt aber auch beim Javascript-Prozess. Vor allem die CPU. Der Prozess kann ein Event nicht mehr vollständig abarbeiten, bevor schon das nächste kommt. Da der js-Controler auf den asynchron laufenden Javascrpt-Adapter warten muss, baut er eine Event-Queue auf. Der Speicherbedarf des js-Controllers steigt (linke Markierung):
Sobald keine Events mehr kommen (Markierung rechts unten), sinkt die Last im System. Doch nicht auf den Augangswert. Es liegt noch eine lange Liste mit sich aufgestauten Events im js-controller vor. Diese wird sukzessive vom JS-Controller abgearbeitet (Markierung rechts oben) und der Speicherbedarf des js-controllers kehrt schließlich wieder zu seinem Ausgangswert zurück (links).
Ach ja, Leerauf ioBroker mit 22 aktiven Adaptern = 0,06% CPU. Da hält auch ein Raspberry mit. :mrgreen:
Bei Interesse ggf. demnächst: History- und SQL-Adapter - wie war das noch mit den 10 MInuten?
-
Sehr interessante Tests.
Was auch interessant wäre: Redis als State Service.
Weil genau Redis halten wir als performance boost für die Kommunikation. Und redis kann auch wo anders laufen (nicht da wo js-controller läuft)
-
Einige ausgewählte Werte aus dem laufenden js-controller würden helfen. Ein Status-Event zur Abfrage. Message Queue-Länge und einige andere. Hast Du Ideen?
-
Hey,
geile Tests!!
Die Annahme ist, wie Bluefox schon angedeutet hat, das der Engpass im js-controller im Handling der State-DB liegt. Die ist für so eine Menge nicht gedacht.
Meine Erwartung wäre das ein Switch auf Redis den Flaschenhals löst. Redis als Softwre ist für solche Datenmengen und viel mehr ausgelegt und "nativ" geschrieben und interpretiert kein JavaScript
Das als vergleichstest wäre genial.
Es ist bekannt das der js-controller ab einer gewissen Eventzahl Problematisch wird und dann empfehlen wir die Nutzung von Redis.
AUch ein herauslösen der State-DB in einen eigenen Prozess wird nicht viel helfen weil im Normalfall der js-controller Hauptsächlich diese State-DB/Objekt-DB Arbeit macht …
Und ja history/SQL wäre sehr interessant, da wären aber erstmal kleinere Datenmengen sinnvoll.
Ich tippe:
-
bei History wird die i/o problematisch beim Schreiben der ganzen Daten - auch weil beim schreiben ein Tages-File komplett gelesen, neue Datensätze angehängt und dann alles neu geschrieben wird. Das kann bei solchen mengen nur nach hinten losgehen.
-
bei SQL wirst Du an das aktuelle 100 parallele Connections Limit bzw was auch immer Du einstellst inkl. in der DB rennen weil hier viel parallel in die DB gepumpt wird
-
bei InfluxDB (stand nicht auf deiner Liste aber auch Meinung) kommst Du nur mit dem "Bündeln" der Datenübertragung weiter, was dann aber wieder auf den Speicher geht. Bei den Datenmengen und dem das Du bedenken musst das die Übertragung auch kurz dauert geht es hier ums Tuning ... ALso der könnte am ehesten durchhalten wenn man Menge rein und "wie raus" optimiert
Ingo F
-
-
Wow… eine super Analyse!
Ein Vergleich mit Redis würde mich auch interessieren.
Redis in der Standardinstallation, wie in der Doku beschrieben.
Reis selbst kann dann auch noch optimiert werden.
Für den Vergleich Filesystem vs. Reis ist das aber erst einmal uninteressant.