Freitag, 14. Dezember 2012

No nodejs für arbeitsintensive Dauerleistung


Die Wunderwaffe für die Webentwicklung ist nicht als Arbeitstier-Skriptwerkzeug zu gebrauchen. Solange Events ankommen und Daten aus dem Arbeitsspeicher gesendet werden sollen, ist nodejs wirklich effizient. Aber stupides Datenauslesen, Dateien anlegen und diese wieder verarbeiten bringt nodejs schnell aus der Puste.

Klar es läuft nur auf einem Thread und das spürt man dann auch. Es arbeitet ineffizient bei vielen Datenzugriffen.

Durch die asynchronen Abläufe kommt es leider auch zu ungewünschten Nebeneffekten. Das Abarbeiten von grossen Aufträgen kann damit zur Tortur mit OutOfMemory werden.

Der Versuch ist einfach: 


- Lese 10.000 Datensätze aus der Datenbank
- Speicher die Daten in Dateien
- Öffne diese Dateien und lese Inhalt ein
- Versende Inhalt per Webrequest
- Erfasse Server-Antwort in Datei
- Benenne Datei mit Zusatz "closed" um

Was machen wir, um dieses Verhalten zu testen?

Dazu ein einfaches Skript,welches Daten aus einer Datenbank zieht. Ob wir nun alle Datensätze abholen und dann über diese iterieren, oder bei jedem Zeilentreffer die Verarbeitung starten ist egal. Diese minimalen Unterschiede können wir ignorieren.

Wir brauchen noch eine Funktion, die den Dateninhalt einer Zeile als JSON abspeichert. Zuerst wählen wir die asynchrone Variante. Die Funktion erstellt eine Datei mit Namen: Datensatz-Id_OPEN.txt. Am Ende dieser Funktionen rufen wir das Abarbeiten der Datei auf. 

Dazu erstellen wir eine weitere Funktion, welche die Datei öffnet, einliest und einen Webserver kontaktiert. Wir senden die Daten als POST-Request. Geht auch sehr einfach mit nodejs. Die Antwort vom Server hängen wir an die Datei und benennen diese wiederum um. Neuer Name: Datensatz-Id_CLOSED.txt.

Warum diese Aufwand? 

Dadurch, dass die schreibende Funktion nicht auch gleich versendet, können wir eine Prozess-Wiederaufnahme Option einbauen. So könnten wir eine separate Funktion starten, welche über die Dateien geht, welche nicht im Zustand (im Namen) CLOSED haben und diese dann verarbeiten. Im Grunde wäre es hier ratsam, noch einen weiteren Status einzuführen: PROCCESSING oder so, damit man auch erkennt, welche direkt in der Mache waren. Können wir uns sparen und für diesen Test nehmen wir an, dass der Server Duplikate verwirft.

Was passiert nach dem Starten vom Skript?

Wir starten das nodejs Skript (ohne Aufräumarbeit durch separeten Aufruf für abgebrochene Übertragungen) und zuerst werden die Daten aus der Datenbank geholt. Das geht sehr schnell, so schnell wie es die DB erlaubt. Dann iterieren wir über die Datensätze und rufen die Funktion auf, zum erstellen der Dateien.

Hier das erste interessante. Damit wir mitbekommen, was passiert geben wir bei jedem Funktionsaufruf eine Message auf der Konsole aus. Wir sehen, dass nun 10.000 mal die Funktionen für das Datei erstellen aufgerufen wird. Aber die Message für das schreiben in die Datei erscheint nicht. Ein Blick in den Ordner zeigt auch, dass tatsächlich keine Datei erstellt wird. Das Skript baut die JSON-Texte auf und hat vor dem  Schreiben scheinbar einfach alles liegen lassen. Nach den 10.000 Meldungen, schreibt er nun tatsächlich. Er beginnt die Dateien anzulegen. Nach dem Anlegen sollte die Verarbeitung starten, doch das passiert nicht. 

Ja klar, ist alles asynchron, jedoch sollte man meinen, dass er nicht erst alle Dateien anlegt, bevor er dann zum Verarbeiten (Senden der Daten an den Webserver) kommt. Doch genau das passiert. Bis hierher kann man noch mehr oder weniger das Verhalten akzeptieren.

Um sicher zu gehen, dass auch nicht unsere Vorgehensweise Schuld ist, ändern wir die Datenbankabfragen-Funktion so, dass nun jede Zeile verarbeitet wird. Doch genau das Gleiche Phänomen. Um nun mehr zu forcieren, wird auch das Schreiben nun synchron gemacht. Das verbessert ganz leicht den Ablauf, aber zu Lasten von Geschwindigkeit. 

Irgendwann beginnt nun dann die Verarbeitung des Sendens. Das Skript beginnt mit dem Lesen und meldet dies auch pflichtbewusst. 10.000 Dateien werden nun geöffnet, ohne dass scheinbar gelesen wird!!! Selbst wenn er die Daten gelesen hat, werden die weiteren Befehlszeilen nicht ausgeführt. Der Speicher läuft, das ganze System bekommt langsam Probleme. 

Erst nachdem scheinbar alle 10.000 Dateien gelesen wurden, beginnt er mit dem Versenden. Doch die Antwort wird nicht erfasst. Stattdessen müssen wir wiederum warten bis alle 10.000 Webrequests versendet sind. Selbst wenn wir das einlesen synchron machen, haben wir spätestens beim "immer" asynchron arbeitenden Web-Request die Probleme. 

Fazit

Nodejs ist super, wenn man einen eigenen flinken Webserver braucht, um einen Dienst bereit zu stellen, der wenig auf der Festplatte arbeitet. Alle File-Operationen sind grausam. Leider ist damit nodejs für mich nicht mehr in der engen Wahl, wenn es darum geht, grosse Datenmengen zu verarbeiten. Das ist nicht sein Metier und man sollte es auch nicht erzwingen. 

Keine Kommentare:

Kommentar veröffentlichen