Saturday, November 5, 2011

Sehr geehrte Headhunter, liebe Personalvermittler

Aus aktuellem Anlass und in der Hoffnung, dass ihr - neben meinem Xing Profil - auch meinen Blog lest, schreibe ich euch diese Zeilen. Ich habe zum Anfang September den Job gewechselt, das habt ihr sicherlich bemerkt. Ich gehe zwar nicht davon aus, dass ihr es in eure "sprech ich ihn jetzt an?"-Überlegung einbezieht, aber ich wollte in meiner grenzenlosen Naivität trotzdem mal darauf aufmerksam machen. Ich frage mich manches Mal ernsthaft, ob ihr nicht lieber hättet weiter euren gelernten Beruf weiter ausüben sollen. Vielleicht wart ihr ja gut darin? Und selbst wenn ihr diesen Beruf gelernt habt, scheinen die Meisten doch wo anders besser aufgehoben. Die Ignoranz, mit der ihr jedoch über mich und mein öffentliches Profil herfallt, kennt leider kaum eine Grenze. Warum hat euch keiner beigebracht Profile auch mal zu interpretieren, nicht nur zu lesen? Selbst Google reagiert besser auf Keywords als ihr! Nur weil ich "XYZ" in meinem Profil stehen habe, seht ihr die Chance mich zum Experten zu erheben und in euer Suchprofil zu pressen? Ich bin gerade von einer Agentur zu einem völlig anders strukturierten Unternehmen gewechselt. Habt ihr mal nachgedacht, ob das vielleicht ... Absicht war? Würde ich in eine Agentur wollen, hätte ich nicht dann auch eine Agentur gewählt? Meint ihr ernsthaft, ich wechsle nach nicht mal zwei Monaten direkt wieder den Job? Kennt ihr das Wort "Treue" oder auch "Nachhaltigkeit"? Wäre es euch nicht genauso wichtig, dass ein Mitarbeiter in dem Unternehmen bleibt, an den ihr in vermittelt? Nunja, die Erwartungshaltung, dass Psychologie oder auch nur grundsätzliches analytisches Verhalten in eure Arbeit mit einfließt, ist wohl doch zu viel erwartet, schließlich ist der Mensch eure Ware und Nachhaltigkeit nicht euer Problem. Leider wandelt sich euer Verhalten mehr und mehr zu dem eines Spam-Bots und meine Reaktion darauf entspricht auch immer mehr diesem Schema - leider gibt es bei Xing noch keinen Spamfilter. Persönliche Ansprachen sind selten geworden, die Stiefelleckerei in euren Texten und eure Penetranz kotzen mich an. Die Zeiten, wo ihr Nerds und Geeks mit Schmeicheleien hinterm Ofen hervor locken konntet, sind nun seit Jahren vorbei.

Ich suche mir meinen Job selbst aus. Und das habe ich bereits getan.

Ich weiß, dass ich einigen Vertretern eurer Zunft Unrecht antue. Leider sind mir bis zum heutigen Tage nur ein oder zwei von euch begegnet, von denen ich das oben gesagte nicht behaupten kann.

Wednesday, November 2, 2011

Ein Newsletter Tool selbst gebaut - Neo4J, RabbitMQ, Vaadin

Seit einiger Zeit arbeite ich jetzt als Java Developer bei der trivago GmbH. Zum ersten Mal durfte ich nun auch an den trivago Developer Days teilnehmen, welche vom Konzept her etwas an die Google'sche 20% Regelung erinnern. Wir Developer bekommen in regelmäßigen Abständen 3-4 Tage Zeit, sich ein Projekt auszuwählen (aus dem internen Ideenportal oder aus eigener Motivation) und dieses Umzusetzen. Für mein erstes DevDays Projekt habe ich mich, zusammen mit Christian Krause (@redbrick) an ein Mail Delivery System gesetzt. Drei Tage (plus ein Halber für das Vorbereiten der Präsentation und ein weiterer Halber für das Setup) sind nun nicht viel, auch wenn man die Definition von "Tag" irgendwo zwischen acht und 14 Stunden ansiedeln kann. Wir haben uns also auf die Kern-Features beschränkt, die wir selbst von einem solchen System erwarten würden. Klar war, dass Mail-Delivery die Kernkompetenz sein sollte. Wir wollten jedoch, auf der Delivery Schicht basierend, auch eine Applikation erstellen, damit der Nicht-Tekkie auch etwas davon hat. So ist die Idee des Newsletter Systems entstanden, welchem wir den Arbeitstitel Mail-Pigeon verpasst haben. Die Wunsch-Features im Überblick: Mail-Delivery:
  • Versand über mehrere Clients (horiz. Skalierbarkeit)
  • Kein Datenverlust beim Absturz einer oder aller Sending-Daemons
  • Bounce Handling (zumindest erst mal rudimentär)
  • Ein Datenformat, dass sowohl von PHP und JavaScript, als auch von Java beherrscht wird. Bleibt nicht viel: XML und JSON. Und XML war es nicht ;)


Newsletter System:
  • Absender Management (Durch die vielen Nationen und Sprachen bei trivago haben wir sehr verschiedene Absender)
  • Empfänger und Empfängergruppen
  • Newsletter und Vorlagen für neue Newsletter sowie Personalisierungsmöglichkeiten
  • Wirkungsgradbestimmung durch Trackingpixel und Kampangen
  • Live Versandstatus und Fortschritt
  • Tagging für einen Versand
Als Technologien kamen Neo4J, eine GraphDB, RabbitMQ, eine MessageQueue und Vaadin, ein auf GWT basierendes UI Framework zum Einsatz. Ich werde alle Technologien, deren Verwendung und eine kurze Begründung zur Wahl im Folgenden anreißen.


RabbitMQ ist ein MessageQueue Server, der von VMWare's SpringSource Devision in Erlang entwickelt wird. Die Wahl fiel auf RabbitMQ, weil er sowohl AMQP und JSONRPC over AMQP, als auch STOMP unterstütz und damit für alle bei trivago eingesetzten Sprachen erreichbar ist. RabbitMQ ist zudem hochperformant und kommt unserem Vorhaben, Tonnen von Mail zu versenden, zu Gute.




Neo4J ist eine Graphdatenbank, geschrieben in Java. Neo4J unterstützt sowohl gerichtete, als auch gewichtete Kanten, ebenso wie Eigenschaften an Knoten. Das Konstrukt wird ergänzt durch einen Lucene Index für Knoten, über den man recht schnelle Key-basierte Lookups durchführen kann. Die Wahl fiel deswegen auf eine GraphDB, weil sie am sinnvollsten unsere Daten abbildet. Es entstehen sehr viele Beziehungen zwischen den Entitäten unserer Applikation, die durch RDBMS - JOIN - Konstrukte sicherlich nur unter hohem Rechenaufwand abbildbar wären.Ein Beispiel ist die Relationsmenge zwischen Empfänger (E), Empfängergruppe (G) und versendetem Newsletter (N). Hier gibt es folgende Relationen abzubilden:
  • N -> versendet an -> G
  • E -> hat empfangen -> N
  • E -> hat geöffnet -> N
  • G -> enthält Empfänger -> E


Nehmen wir noch einen weiteren Knotentype "Bounce" (B) hinzu, wird das Konstrukt noch etwas interessanter:
  • N <- ist hard-bounced <- B -> ist hard-bounced -> E
  • N <- ist soft-bounced <- B -> ist soft-bounced -> E

Damit lässt sich also recht schnell darstellen, welcher Bounce-Typ bei welchem  Newsletter oder Empfänger vorkam. Kanten lassen sich übrigen mit linearem Aufwand zählen, wenn man den Knoten kennt!

Vaadin ist ein auf dem Google Web Toolkit basierendes UI Framework. Ich bin kein großer HTML/CSS Künstler, also musste etwas anderes her, was erwachsen und optisch ansprechend war. Mit Vaadin konnte ich im Liferay - Bereich schon mal Erfahrungen sammeln und fand es schon damals sehr ansprechend. Wer mal Swing oder AWT benutzt hat, der wird sich bei Vaadin recht schnell heimisch fühlen. Jedenfalls bekommt man bei Vaadin jede Menge "geschenkt", was Optik, Kompatibilität und Technologie angeht. Ich kann euch nur den Sampler ans Herz legen, dann versteht ihr was ich meine ;)

Personalisierung  haben wir durch das Einbetten von Apache's Velocity erreicht. Die Mail Templates, aus denen man neue Newsletter erstellen kann, können in Velocity geschrieben werden. Beim Versand wird der Template-Context mit den Daten des Empfängers, der Gruppe, der Kampange und Metadaten befüllt und für jeden Empfänger evaluiert. So wird eine komplette Personalisierung ermöglicht.


Tracking wird durch das automatische Einfügen eines <img /> Tags in die HTML Version der Mail erledigt. Das Image ruft ein separates Servlet auf, welches den Newsletter und die Empfänger ID benötigt. In der Folge kann dann die gerichtete Kante E -> hat geöffnet -> N erstellt werden.


Tagging erscheint vielleicht auf den ersten Blick für einen Newsletter ein wenig sinnfrei. Wenn man jedoch die GraphDB in die Rechnung
mit einbezieht und das Tracking hinzu nimmt, dann entsteht die Möglichkeit "User-Interest-Tracking". Die Kobination Tag (T) sieht dann z. B. folgendermaßen aus (Die Pipe soll zwei Kanten darstellen):




T <- hat Tag <- N <- hat empfangen | hat geöffnet <- E.

Aus diesen Relationen können wir also, bei entsprechender Skalierung, ablesen, für welche Tags sich ein Empfänger interessiert.

Tuesday, July 12, 2011

XML, Text, Hadoop, CouchDB und die Google Search Engine

Größere Datenmengen bringen deutlich andere Herausforderungen mit sich. Durch eine Teststellung ergab sich eine Herausforderung, deren Lösung eine kleine Evolution durchgemacht hat. Ziel des Vorhabens ist, jedes Element aus der ersten Menge mit einer unbekannten Anzahl von weiteren Informationen aus der zweiten Menge anzureichern und das Ergebnis zu einer für die Google Search Appliance brauchbaren HTML Seite zu vereinen.

Ausgangssituation
  • 18 Gbyte XML Daten in einzelnen Dateien
  • ca. 60 Gbyte “Metadaten” in einzelnen Dateien (JPG, HTM, XML) und einer 1:n Zuordnung zu den XML Daten aus dem ersten Punkt

In den XML Daten, nennen wir sie mal “Produkte”, gibt es zu jedem Produkt verschiedene Typen von Metadaten. Dies können Bilder, erweiterte Beschreibungen und sonstige Zusatzinformationen sein. Jedes Produkt hat mindestens eine eindeutige ID aus einem von zwei Nummernkreisen. Diese ID findet sich ebenfalls im Dateinamen der zugehörigen Metadaten wieder. Die Anzahl der Produkte liegt bei ca. 5,7 Mio.

Beispiel: Produkt 4711 -> Produktbilder/Bild_4711_Ansicht1.jpg

Der erste Ansatz ist es, bei einer Iteration durch alle Produkte, jeweils alle Metadaten zu finden. Hier kann ein kleiner Shell Aufruf schon ausreichen:


find . -name "*_4711_*" -type f


Dabei sollte 4711 natürlich durch den aktuellen Wert des Produktes ersetzt werden. Wer nun die Hände über dem Kopf zusammenschlägt und I/O Probleme wittert, dem sei gesagt … yes! Ist verflucht lahm.

Kurz ein paar Variabeln definiert

p = Produkt

C(p) = Count(Produkt) -> ca. 5.700.000

m = eine Metainformation

C(m) = Count(m) Gesamtanzahl der Metadaten -> ca. 25.000.000

k = Konstante für die Kosten des I/O Seek auf der HDD, hier in Sekunden gemessen.

Laufzeiten

Bei einer Laufzeit von O(C(p) * C(m)) ist die Platte (Seek I/O) hier sicherlich noch als Konstante zu sehen, womit sich O(C(p) * C(m) * k) ergibt und nach erster Beobachtung k > 8 ist. Ohne es bewiesen zu haben, liegt die errechnete Laufzeit bei weit mehr als einem Jahr ;).

Die nächste Variante war per find ein Directory Index zu erstellen, also find über alle Verzeichnisse zu schicken und das Ergebnis in Textdateien zu Speichern. Diese Textdateien kann man mit grep durchsuchen. Auch hier entsteht eine Laufzeit von O(C(p) * C(m) * k), wobei sich k = 8 herausstellte. Auch hier liegt die Laufzeit bei ca. einem Jahr.

Die Anzahl der Metadaten pro Produkt spielt keine Rolle, da immer die gesamte Menge an Metadaten durchsucht werden muss.

Das Problem bei beiden Ansätzen besteht deutlich in der Multiplikation der Ergebnismengen. 142.500.000 Operationen sind auch ohne Konstante schon eine viel zu große Menge.

Was kann man also tun, damit man die Multiplikation los wird? Es müsste eine Möglichkeit geschaffen werden, dass der Iterator nur eine “Anfrage” an die Ergbnismenge stellen muss und daraufhin alle Metadaten zurück geliefert werden - alternativ ist die Menge 0 auch eine valide Antwort. Um dies zu erreichen brauchen wir einen Index, der auf Basis eines Schlüssels eine Liste von Werten zurück geben kann. Dieser Index muss einmalig befüllt werden. Die technologische Entscheidung fiel hier auf Hadoop für die Vorbereitung der Daten, ein PHP Script zum Einfügen der Daten in den Index und eine CouchDB als eigentlicher Index. Vorteil bei dieser Konstellation ist, dass CouchDB über HTTP angesprochen werden kann. Es gibt also nur zwei Möglichkeiten für die Antwort auf die Frage nach dem Schlüssel: HTTP 200 OK oder HTTP 404 NOT FOUND. Sicherlich könnte man dies auch über Redis abbilden, jedoch war die Anbindung der CouchDB einfacher und schneller für diesen Anwendungfall.

Wir führen neue Variabeln ein

h = Vorbereitungszeit Hadoop Map & Reduce

v = Vorbereitungszeit für den Index

r = Konstante für die Kosten eines HTTP Requests, ebenfalls in Sekunden gemessen.

Wenn wir unsere Rechnung unter den neuen Gesichtspunkten aufsetzen, ergibt sich O(h + v + C(p) * r). Durch die Nutzung der Loopback Device konnten wir r = 0.2 erreichen. Die Konstanten h und v sind im Vergleich zur voherigen Multiplikation nahezu lächerlich klein. h liegt bei 146 Sekunden und v bei ca. 600 Sekunden.

Hadoop als Vorbereiter

Die im find vorbereiteten Daten werden nun zu 60Mbyte Blöcken zusammengefasst. Hierbei hilft uns ein kleines PHP Script. Man kann hier auch die von Hadoop angebotene Klasse CombineFileInputFormat verwenden. Die neu geschaffenen Dateien kommen dann auf das HDFS und sind damit für den Map & Reduce Schritt verfügbar.

Der Map Job ist recht simpel. Das Ergebnis des finds wird zeilenweise an die Map Methode geliefert. Der LongWritable key ist der byte-Offset, der Text value ist die Zeile selbst. Wenn der Mapper alle Values, in unserem Fall also die Pfade, zu den Keys, also den IDs, gefunden hat, wird das Ergebnis an den Reducer weitergeleitet. Der Reducer iteriert über alle Values zum Key und erstellt bereit einen String, der später als Liste in JSON weiterverwendet werden kann.
Die Ausgabedatei, die der Reducer schreibt ist ca. 300 Mbyte groß. Den Abschluss der Vorbereitung ist wieder ein PHP Script, welches pro 10.000 Zeilen der Ausgabedatei einen Batch Request an die CouchDB absetzt. Für die 5.700.000 Zeilen, die die Ausgabedatei beseitzt, sind das ca. 600 Sekunden.

Der abschließende Prozess ist nun die Zusammenführung von Produkten und Metadaten. Hierzu gibt es ein Java Projekt, dessen Sourcen ich hier leider nicht zeigen darf ;). Es entstehen HTML Seiten und Link-übersichtsseiten, welche der GSA zum Crawling vorgesetzt werden.

Dieser Crawling Prozess dauert unter Verwendung von CouchDB nur noch ca. 3 Tage statt mehr als 400 Tage. Ein deutlicher Gewinn ;).
Bei nächster Gelegenheit schreibe ich noch ein FollowUp zusammen, da eine Alternative zu CouchDB auch Redis sein könnte.

Sunday, June 26, 2011

Kreativität sucht Perspektiven

Das Schöne an unserem Handwerk ist, dass es so facettenreich ist. Nicht nur die Arbeit, die wir jeden Tag vollbringen, sonder auch die Herangehensweisen an diese Arbeit. Der Eine sieht sich als 9-to-5 - Typ und macht wie ihm aufgetragen wurde. Meist sind diese Menschen fest an einen Wissenstand gebunden und haben "das ja schon immer so gemacht". Um es scherzhaft zu sagen, haben diese Leute einen Horizont, der mit einem Kreis mit dem Radius von null beschrieben werden kann - auch einfach "Standpunkt" genannt. Andere sehen sich als progressiv an, bilden sich weiter und nutzen neue Projekte um es besser zu machen als im Vorherigen. Ich behaupte, dass mit einem Großteil solcher mitarbeiter viele Unternehmen und Agenturen gut über die Runden kommen, jedoch außer der Unternehmensgröße bzw. dem Umsatz, sich nichts Wesentliches bewegt. Wiederum Andere versuchen neues zu schaffen und neue Wege zu beschreiten. Wenn jetzt einigen das Buzzword "innovationsgetrieben" vorschwebt muss ich wahrscheinlich deutlicher werden, da ich es eher als kreativ bezeichne. Nur am oberen Ende der Innovationsleiter zu stehen ist nicht allein das, was für mich Kreativität bedeutet. Kreativität ist auch, sich mit der neuen Anwendbarkeit von Bekanntem zu beschäftigen, Innovationen einfließen lassen und einfach den Ansatz von "das haben wir schon immer so gemacht" fallen zu lassen.


Ein gutes Beispiel ist die NoSQL Bewegung. Als falsch empfinde ich es, wenn man nun versucht jede Datenspeicherung in Dokumente, Key-Values, Wide-Columns oder Graphen zu pressen. Hier kann man doch als erstes überlegen, was man in der Vergangenheit vielleicht in ein relationales Modell gepresst hat und es darauf hin auf ein passenderes NoSQL Format umstellen. Kreativität ist für mich also auch Bestehendes in Frage zu stellen.


Regelmäßig treffen in der IT zwei Welten aufeinander. Aus Projektmanagersicht leiden Entwickler am Elfenbeinturm-Syndrom. Alles muss perfekt sein, skalierbar sein. Man will sich mit der guten Arbeit auch profilieren können, Stolz empfinden dürfen. Der Entwickler jedoch sieht mit einer gewissen Grundangst dem Projektmanager ins Gesicht und wartet darauf, dass Qualität zu Gunsten des Preises oder der Zeit minimiert wird oder der kreative Spielraum zu Gunsten einer kostengünstigen und weniger risikobehafteten Lösung eingeschränkt wird. Die Darstellung mag vielleicht übertrieben erscheinen, aber das soll sie auch. Risiko ist das Stichwort. Kreative Herangehensweisen sind risikobehaftet und Risiko wird vom Management in Geld gemessen. Beim Thema Finanzen wird es dann meist sehr schnell ruhig. Kreativität darf kein Geld kosten - womit sich jegliche Diskussion für mich erledigt. Wenn Kreativität und Innovation kein Geld kosten dürften, würden wir heute alle noch den VW Käfer der ersten Serie fahren. Der funktionierte doch wunderbar.


Ein Unternehmen muss also die Wahl treffen, ob es kreative Entwickler unterstützt und damit auch Risiken eingeht oder ob es keine zusätzlichen Risiken eingehen möchte und damit die Gefahr eingeht, kreative Entwickler einzuschränken und auf Dauer zu verlieren. Also die Leute zu verlieren, die in den Momenten, wo neue Ansätze gebraucht werden, da sind und vielleicht die zündende Idee haben, die Leute zu verlieren, die einfach Ideen umsetzen die Geld bringen oder Geld sparen können.


Klar scheint mir zu sein, dass Vorreiter in der Branche sich nicht durch konservatives Verhalten hervorbringen, es liegt jedoch in der Entscheidung jedes Unternehmens wie ernst es seine sich erdachte Rolle nehmen will.

Kreativität sucht Perspektiven.