Grundlagen zum Domain Name Service

Wie ich beim Schreiben meiner Artikel gemerkt habe, ist es sinnvoll gewisse Grundlagen einmal abzuspalten, um darauf verweisen zu können. Ich werde daher immer einmal wieder den ein oder anderen Grundlagen-Artikel hier zusammenschreiben, der im ersten Moment vielleicht etwas aus dem Zusammenhang gerissen scheint, aber ggf. wertvolle Informationen für alle Einsteiger enthält oder auch dem ein oder anderen Erfahrenen als Auffrischung dienen kann. Ich versuche das möglichst prägnant und wo immer möglich an Beispielen zu beschreiben.

In diesem Artikel geht es im um das Domain Name System – das Konstrukt welches zu Arbeit am Rechner etwas leichter macht, da sich das menschliche Gehirn in der Regel Namen besser merken kann als Zahlenfolgen.

Der langjährige Standard für DNS-Server im Unix und Linux-Umfeld ist der BIND-Daemon und so ziemlich jeder der sich schon einmal mit DNS-Namen hat etwas eingehender beschäftigen dürfen (oder müssen), der weiß, dass die Syntax der Config-Files nicht gerade in die Kategorie “leicht verdaulich” fällt. Zudem war BIND immer mal wieder ein Sorgenkind wenn es um Sicherheitslücken ging. Sehr verständlich, dass sich viele Website-Betreiber da lieber auf einen Hoster verlassen der das schon für sie regeln wird. Für mich auch lange ein valider Weg, aber eben auch einer mit Einschränkungen.

Continue reading

Datenbankversionierung – Vor- und Nachteile bestimmter Praktiken

Gerade hatte ich mal wieder ein Schlüsselerlebnis in meiner Programmierung. In diesem Fall gab es ein Kollision mit YAML (YAML Ain’t Markup Language) – ich spreche explizit von Kollision, denn meine Erfahrung ist für den verwendeten Anwendungsfall einfach nur schlecht bzw. ich sehe leider aktuell einige Nachteile.

Datenbank-Schemata versionieren

Zur Versionierung von Datenbank-Änderungen bzw. um den Weg von einer “leeren” Datenbank bis hin zum aktuellen Stand der Entwicklung zu beschreiben verwendet man in der Regel entsprechende Tools wie Liquibase oder Flyway. Das Prinzip ist im Prinzip immer das Gleiche – mit ein wenig “Rahmenwerk” drum herum steuert man die Ausführung der Datenbank-Statements. Dabei kann man auf verschiedene Szenarien eingehen, einer der häufigst genannten ist, dass man ein Produkt mit verschiedenen Datenbank-Servern laufen lassen kann. Das ist beispielsweise wichtig wenn man Software an Firmen (seltener private Anwender) ausliefert, sich aber darauf einstellen muss, dass dort nicht unbedingt die aktuelle Version von MySQL läuft sondern eben im Zweifel auch ein PostgreSQL oder eine andere (kommerzielle) Datenbank wie Oracle. Continue reading

Container und IPv6 – Basics zum Verständnis / Netzwerkzuschnitt

In diesem Beitrag erläutere ich erst einmal die grundlegenden Probleme welche sich beim Einsatz von Container-Lösungen im Netzwerk ergeben und wie man sie sinnvoller Weise technisch lösen kann. Schlüssel hierfür ist die Verwendung von IPv6 anstelle von IPv4. In einem weiteren Artikel beschreibe ich dann die konkrete Umsetzung. Wer sich mit IPv6 und Containern bereits sicher fühlt kann diesen Artikel ggf. überspringen.

Container sind der aktuelle Stand der Technik was die Auslieferung und die Laufzeitumgebung für Serversoftware betrifft. Die Para-Virtualisierung löst einige Probleme die man typischer Weise hatte, unter anderem das Alles-oder-Nichts-Problem, wenn es darum geht Systemsoftware zu aktualisieren. Mit jedem größeren Versionssprung gibt es die Gefahr, dass bestimmte Teile des eigenen Codes nicht mehr laufen. Aktuell gibt es immer noch genügend Projekte und Software welche auf ältere (um nicht zu sagen teilweise uralte) Versionen von PHP angewiesen sind um zu funktionieren. Mit dem Versionssprung von 5.x nach 7.0 sind einige Altlasten und Funktionen über Bord geworfen worden, unter anderem auch der gesamte Stack für die MySQL-Anbindung, was einigen Anwendungen dann doch das Genick bricht. Häufig stand man nun vor dem Problem, für einen Teil des gehosteten Angebots die Version anheben zu wollen, aber die Altlasten müsste man dafür erst einmal auf Stand bringen. Das ist nicht immer ohne weiteres möglich. Mit Containern schafft man sich hier ggf. kleinere Services mit jeweils ihrer maßgeschneiderten Umgebung – inklusive abgeschottetem Datenbankserver, den sich nicht mehr alle Angebote teilen müssen (über die Vor- und Nachteile von Datenbanken in Containern kann man einen eigenen Artikel schreiben).

Continue reading

Rasperry Pi 3B+ mit Samsung LE22S86BD

Bereits seit einiger Zeit habe ich einen Rasperry Pi, nachdem ich bereits einiges damit herumgespielt hatte soll er nun endlich seinen Stammplatz als Medienzuspieler am Fernseher bekommen, damit man nicht jedesmal einen Laptop anschließen muss, nur um einen Stream zu schauen. Zumal ich ja sagen muss, dass das aktuelle Fernsehprogramm mich ohnehin nicht vom Hocker haut und bei meiner ganzen sportlichen Aktivität bleibt mit selten die Zeit zum Fernsehschauen.

Auch daher muss es erst einmal der aktuell vorhandene Fernseher tun, ein etwas älteres Modell von Samsung, Modell LE22S86BD. Die Einrichtung des Raspi habe ich natürlich am Monitor mit samt Tastatur, Maus und Netzwerk durchgeführt und nicht am Fernseher. Das Ergebnis ist dann leider auch gar nicht Plug&Play sondern eher fühlt man sich in die Zeiten des “Plug&Pray” zurück versetzt. Denn nach dem Anschluss per HDMI sieht man nahezu nichts, genauer gesagt nur eine Fehlermeldung auf dem LCD-TV “Mode not supported”. Da kommen bei mir prompt Erinnerungen an die ersten Gehversuche mit Xfree86 als Grafikserver – damals musste man auch mit viel Trial&Error die richtigen Einstellungen für Grafikkarte und Monitor herausfinden. Wenn man das übertrieben hat, konnte man mit den Versuchen sogar durchaus Geräte dauerhaft unbrauchbar machen.

Mit folgenden Einstellungen in der “/boot/config.txt” bekommt man bei diesem Fernseher ein formatfüllendes Bild ohne Trauerränder hin:

hdmi_group=1 # CEA => TV-modes
hdmi_mode=5 # 1080i, 60Hz 
disable_overscan=1 # TV does not support overscan

Das ist die einzige Kombination die ich zuverlässig ans Laufen bekommen habe, auch wenn “tvservice” durchaus andere modi meldet:


pi@raspberrypi:~ $ tvservice -m DMT
Group DMT has 5 modes:
mode 4: 640x480 @ 60Hz 4:3, clock:25MHz progressive
mode 9: 800x600 @ 60Hz 4:3, clock:40MHz progressive
mode 16: 1024x768 @ 60Hz 4:3, clock:65MHz progressive
mode 35: 1280x1024 @ 60Hz 5:4, clock:108MHz progressive
mode 57: 1680x1050 @ 60Hz 16:10, clock:119MHz progressive


pi@raspberrypi:~ $ tvservice -m CEA
Group CEA has 9 modes:
mode 1: 640x480 @ 60Hz 4:3, clock:25MHz progressive
mode 2: 720x480 @ 60Hz 4:3, clock:27MHz progressive
mode 3: 720x480 @ 60Hz 16:9, clock:27MHz progressive
(native) mode 4: 1280x720 @ 60Hz 16:9, clock:74MHz progressive
(prefer) mode 5: 1920x1080 @ 60Hz 16:9, clock:74MHz interlaced
mode 17: 720x576 @ 50Hz 4:3, clock:27MHz progressive
mode 18: 720x576 @ 50Hz 16:9, clock:27MHz progressive
mode 19: 1280x720 @ 50Hz 16:9, clock:74MHz progressive
mode 20: 1920x1080 @ 50Hz 16:9, clock:74MHz interlaced
pi@raspberrypi:~ $

Falls also irgendjemand noch diese Kombination betreiben will, ich hoffe ich erspare ihm hiermit eine ziemlich mühsame Ausprobiererei – Kommentare und weiter funktionierende Modi sind natürlich herzlich willkommen.

 

Frameworks in der Programmierung – wirklich so essentiell wie immer behauptet?

Wenn immer man sich aktuell umschaut auf dem Jobmarkt, dann sind im Softwarebereich immer Erfahrungen mit Frameworks gefordert oder zumindest gern gesehen. Nun habe ich ja auch einige Erfahrung und ich sehe das immer wieder etwas zwiegespalten.

Machen wir uns erst einmal ein Bild mit welchem Ziel Frameworks entstanden sind. Mehrheitlich ist ihr Ziel die Entwicklung von Software zu vereinfachen und zu beschleunigen. Der Entwickler soll sich weniger Gedanken machen müssen über einige technische Details und soll sich ganz und gar auf seine Applikation konzentrieren können. Das klingt ja erst einmal nicht schlecht. Wer möchte sich schon gerne mit den Details einer TCP/IP-Verbindung womöglich sogar noch Verschlüsselung beschäftigen müssen wenn er nur Daten von einem Server irgendwo im Netz abholen möchte. Das klingt doch schon insgesamt sehr verlockend, aber jede Medaille hat bekanntlich zwei Seiten. Jede Technologie bringt ihre Tücken und ggf. auch Einschränkungen mit. Eine dieser Einschränkungen ist es, dass ein Framework gewisse Regeln vorgibt an die man sich halten muss, wenn man es verwendet. Das muss noch nicht mal negativ sein, vermeidet es doch, dass man sich unfreiwillig irgendwo eine Bremse einbaut, die man ggf. später wieder ausbauen muss.

Da ich relativ viel im Bereich Webentwicklung mache, beschränke ich mich im Folgenden bei den Beispielen auf dort häufig anzutreffende Frameworks und meine Erfahrungen mit Ihnen.

Eines der für mich wichtigsten Frameworks ist und bleibt JQuery und es ist eines der wenigen Frameworks, das ich schätzen gelernt habe. Warum das so ist? JQuery schreibt mir als Entwickler nicht irgendein Gerüst vor, vielmehr gibt es eine Reihe von best-practices und jede Menge Beispiele. Brauche ich aber nur eine simples Ein- und Ausblenden eines Formularelements dann muss ich mich nicht erst mit dem Bauen und Administrieren von Formularen im Sinne des Frameworks beschäftigen. Stattdessen kann ich es einfach aus und einblenden wie es gerade nötig ist. Minimaler, schlanker Code mit dem ich das Element finden und damit etwas machen kann. Derartige Abstraktion (noch dazu über Browser-Grenzen hinweg) finde ich sehr hilfreich. Auch wenn sich JQuery selbst als Framework bezeichnet – für mich hat es eher den Charakter einer umfangreichen Library (aber Library klingt natürlich so altbacken, das kannten ja schon die Entwickler aus C … daher kann man das natürlich in einem modernen Umfeld nicht offiziell so nennen).

Das nächste Framwork, mit dem ich auch immer noch arbeiten darf, ist ZendFramework. Mittlerweile ist davon die Version 3 auf dem Markt, vor allem nachdem die Version 2 reichlich wenig Akzeptanz gefunden hat. Im Arbeitsumfeld sind wir noch größtenteils mit Zend1 unterwegs. Das wird mittlerweile definitiv nicht mehr weiter entwickelt, aber wenn es läuft und es keine sicherheitskritischen Probleme gibt gilt die alte Weisheit: “never touch a running system”. Leider muss ich sagen, dass mir das Framework häufig mehr Arbeit verursacht als es je einsparen könnte. Vieles ist einfach nur sehr umständlich, trotz oder gerade wegen der vorgegeben Struktur. Auf der einen Seite ist sie recht starr – es gibt nur eine Möglichkeit bestimmte Ergebnisse zu erreichen. Auf der anderen Seite sind die Funktionen dann doch recht häufig wachsweich – nahezu an jeder Stelle kann (oder muss) man Parameter als indiziertes Array übergeben.

Richtig enttäuscht hat mich das Framework dann bei der Umsetzung von Commandozeilen-Parametern. Im Quellcode des Framworks stehen noch jede Menge offene Todo-Punkte. Die Funktion an und für sich kann man “out of the box” fast nicht gebrauchen, denn unbekannte Parametern führen zwangsweise zu einem Abbruch der Verarbeitung. Man kann natürlich dann kurzerhand die Framework-Klasse als Basis hernehmen und die notwendigen Routinen in einer eigenen Klasse überschreiben. Aber so wirklich prickelnd ist das nicht, noch dazu bindet man sich damit sehr stark an das Framework – will man die Funktionalität in einem anderen Projekt wieder verwenden, so muss man die dort mit hoher Wahrscheinlichkeit neu implementieren. Ich habe mich in diesem Falle dazu entschieden, es dann gleich selbst zu machen, PHP bietet von Haus aus bereits recht umfangreiche Funktionen, unter anderem sei hier Getopt genannt.

Natürlich wollte ich auch mal schauen was aktuelle Frameworks so können und vielleicht machen die es ja besser. Also habe ich mir Symphony angeschaut. Auf den ersten Blick ist wieder alles “nice and shiny”. Sobald man aber etwas damit arbeiten möchte, musste ich feststellen: Die Doku und die Beispiele sind einfach nur sehr akademisch gehalten. Ich habe das Framework nach einigen Versuchen ad acta gelegt, weil es ein verdammt hoher Aufwand ist, einen recht simplen Fall abzubilden: Man nehme eine einfache 1:n-Relation wie sie jede Datenbank ohne Probleme abbildet: Eine Art News-Artikel wird einer Kategorie zugeordnet. Ein priviligierter Nutzer ist in der Lage die Tabelle der Kategorien bei Bedarf zu erweitern. Ein klassischer Fall, den man im User-Interface gerne und effizient mit Drop-Down-Menu realisiert. In Symfony ist das ein echter Krampf, spätestens bei der Verwendung der Anzeigenamen als Array-Indizes und der eindeutigen Werte als Array-Werte habe ich mir nur noch an den Kopf gelangt. Das funktioniert zwar dank UTF-8-Unterstützung aber es widerspricht sämtlicher Intuition. Die Beispiele zu Formularen sind da auch keine wirkliche Hilfe – da wird einfach darauf gesetzt, dass man es ja doch per Text umsetzen könnte. Das mag für schnelle Projekte sinnvoll sein, aber im Businessumfeld sind die Regeln dann doch etwas strikter. Man kann es natürlich auf der Server-Seite dann wieder prüfen und eine Fehlermeldung ausspucken – aber warum muss der Anwender erst Tippfehler machen und ggf. raten wenn es nur wenige valide Auswahlen gibt?

Das ist jetzt sicherlich keine umfassende Evaluation, aber in vielen Punkten gleichen sich die Frameworks dann doch. Ich werde mir auch noch weitere anschauen. In einem der nächsten Posts werde ich einige generelle Kritikpunkte die mir bisher bei nahezu jedem “Backend-Framework” aufgefallen sind näher ausführen.

 

Fiese Falle in MySQL bei Verwendung von Views in (Left)-Joins

Da habe ich doch kurz vor Weihnachten noch eine echte Knobelaufgabe erlebt, deren Ergebnis für den ein oder anderen sicherlich interessant sein dürfte. Ausgangspunkt war folgendes eigentlich simple SQL-Statement:

Select * from tableA left join viewC 
     on tableA.id=viewC.lookup

Soweit so unspektakulär, auch wenn das Original noch mit einigen weiteren Filtern gespickt war und die View nicht gerade ein einfaches Query kappselt.

Was allerdings gar nicht lustig war, waren die Ergebnisse dieser Abfrage, denn sie entsprachen nicht den Erwartungen. Aus der View erhielt ich nämlich keinen Join-Partner obwohl ich diesen als Test extra geprüft hatte mit:

 Select * from viewC where lookup='TestValue'

Folgende Punkte habe ich als “übliche Verdächtige” abgeprüft:

  • Vergleich der Datentypen – auch nicht die Ursache
  • Collations (also Zeichensätze) – auch mit Casting, das Ergebnis bleibt das Gleiche

Spannender Nebeneffekt, der bei der weiteren Untersuchung heraus kam – nutze ich die view nicht sondern packe 1:1 den Code aus der Definition mit in mein Statement

 
Select * from tableA left join 
   (select * from tableB 
     left join tableF 
         on tableB.id=tableF.foo 
      where tableF.bar='baz' and deletedDate is null)
   as testview on testview.id=tableA.id

Dann funktioniert wieder alles wie erwartet. Es staunt der Laie und der Fachmann wundert sich. Höchste Zeit das Problem genauer unter die Lupe zu nehmen und die bekannten Bugs von MySQL bzw. den Deviraten wie MariaDB und Percona zu durchforsten. Ergebnisse sind leider recht dürftig und beschreiben nicht das was ich erlebe. Immerhin stoße ich auf weitere Probleme die erst einmal völlig losgelöst von der Thematik erscheinen, es geht dabei um möglich Einstellungen des Servers und den Standard-Verhalten. Das hat sich in den letzten Jahren immer mal wieder etwas verändert und spiegelt des “Erwachsen werden” von MySQL wieder. Wir erinnern uns: Es hat eine geraume Zeit gedauert bis MySQL endlich als Standard auf INNODB als Engine gewechselt hat und somit referenzielle Integrität in Form von Transaktionen und Foreign Keys unterstützt wurden. Ebenso aus der grauen Urzeit von MySQL stammen Probleme mit NULL-Werten bei Datumsfeldern (DATE,DATETIME). Und genau da liegt der Hase am Ende auch begraben. Die Server-Konfiguration gibt vor, dass der Server sich strikt verhalten soll, unter anderem bei den Datumsfeldern.
Somit ist es nicht mehr zulässig den “uralten NULL-Ersatz 0000-00-00 00:00:00” zu verwenden. Die Routinen für den Ersatz kann man aber per Session einschalten, und genau das ist bei der manuellen Abfrage der Werte jedes mal passiert, sei es per manuellem Join oder bei der Abfrage der einzelnen Tabellen. Dabei verhält sich das isnull je nach eingestelltem Modus. Mal liefert es Zeilen mit “0000-00-00 00:00:00” als Ergebnis, mal nicht, je nachdem ob strict-mode gesetzt ist oder nicht.

Warum läuft es dann beim Verwenden der View aus dem Ruder? Das liegt an der Art und Weise wie MySQL mit Views umgeht: Sind diese übermäßig komplex werden sie als separater Thread gestartet, der eine temporäre Tabelle bereitstellt auf die dann wiederum gejoined werden kann. Dieser Thread wird allerdings vom Serverprozess angestoßen, und dort steht der Modus per default auf strict und dieser wird dann auch angewandt. Somit ist das Ergebnis nicht das was ich bei der singulären Abfrage erhalten habe, sondern leider eben ein leeres da in der Tabelle anstelle NULL eben noch “0000-00-00 00:00:00” verwendet wird.

Die temporäre Lösung ist, den Join manuell auszuführen, mittelfristig werden wir den Server umstellen. Das sind aber eigentlich nur kosmetische Lösungen. Die eigentlich korrekte Maßnahme für dieses Problem ist die Verwendung von sauberen NULL-Werten anstelle obskurer Workarounds. Leider verlassen sich sehr viele Applikationen bei uns noch auf die Tabelle und diese kruden Werte, einfach Umstellen ist also erstmal leider nicht drin. Ich kann jedem Entwickler nur wärmstens empfehlen, sich auf derartige Altlasten nicht zu verlassen und schnellstmöglich dafür zu sorgen diese abzubauen.

Für und Wider Microservices

Serviceorientierte Architektur – das Buzzword ist in alle Munde. Auch ich darf mich regelmäßig im Rahmen meiner Arbeit mit diesem Architektur-Typ auseinander setzen.

Die Idee an und für sich ist ja schon interessant: Funktionen werden über wohldefinierte Schnittstellen (z.B. REST) angeboten, wie die Antwort zu einem Request entsteht ist dann völlig unerheblich – das reicht von einer anderen Programmiersprache bis hin zu einem vollständig anderen physikalischen System. Modularisierung auf die Spitze getrieben. Eigentlich der Traum eines jeden Entwicklers sollte man meinen. Minimale Kopplung, wenn ein Service ersetzt werden soll, muss nur noch die Schnittstelle erhalten bleiben, und man kann es sogar ganz einfach umstellen.

Klingt ja erst mal alles vernünftig, aber jede Medaille (und jede Technik) hat bekanntlich zwei Seiten. Wo ist die Schattenseite der Service-Orientierung?

Ein kurzer Ausflug in die Historie hilft uns das Konzept besser zu verstehen. Schon seit geraumer Zeit haben wir uns an eine Art Gewaltenteilung gewöhnt: Daten “leben” in der Regel in einem eigenen Umfeld, das unterschiedlich ausgestaltet sein kann – sei es eine relationale Datenbank (die es wiederum in verschiedensten Geschmacksrichtungen gibt: MySQL, MariaDB, Postgreß, DB2, etc), in Dateien oder auch in den modernen NoSQL-Datenbanken. Wichtig dabei ist nur: Man sollte die Datenhaltung so wählen, dass sie zum Anwendungszweck passt. Der Blick über den Tellerrand hat auch hier noch nie geschadet: Nur weil man bisher alles in ein relationales Schema gepresst hat, muss das für eine (neue) Anwendung noch lange nicht eine gute Wahl sein.

Ohne es sich groß bewusst zu machen, haben wir hier die erste Stufe einer Service-Orientierung eingeführt – angesprochen wird der Service über eine definierte Schnittstelle. In ganz vielen Fällen ist die Schnittstelle in der Sprache der eigentlichen Applikation bereits etwas abstrahiert um sie leichter nutzen zu können (auch bekannt als “Connector”). Ein ähnliches Konstrukt mit einem anderen Focus hat bereits Dan Bernstein für qmail verwendet. Triebfeder dort war die Sicherheit – auch das bietet eine definierte, gekapselte Schnittstelle.

Ist die Sache mit den Services also nur “neuer Wein in alten Schläuchen”? Ganz so einfach ist es nicht, es hat sich doch einiges geändert – die Schnittstellen haben sich verändert – weg von festen Techniken wie etwa einem MySQL-Adapter – hin zu gerneralisierten Schnittstellen wie SOAP oder JSON als Transport-Kappsel. Das hat natürlich Vorteile – man kann die Services etwas weiter auseinander ziehen, z.B. auf spezielle Server mit angepasster Hardware oder etwa einen zentralen Authentifizierungs-Server für Single-Sign-On.

Ein weiterer oftmals genannter Vorteil ist die Skalierbarkeit: Wenn mehrere Rechner den gleichen Service anbieten, dann kann man die Last darauf gleichmäßig verteilen. Besonders sinnvoll ist es wenn man die Server-Funktion dann noch fachlich genauer eingrenzen kann. So kann man sich vorstellen, dass man mehrere Server aufsetzt und dann alphabetisch anhand der Nutzernamen aufteilt – natürlich so, dass jeder Server im Zweifel die Aufgaben eines anderen nutzen kann – aber durch die Spezialisierung wirken Cache-Mechanismen um ein Vielfaches besser.

Soweit die vielen positiven Aspekte (es gibt noch einige mehr). Aber jede Medaille hat ja zwei Seiten. Was sind Nachteile einer derartigen Struktur?

Ausfallsicherheit gibt es nicht ohne Preis – es müssen ggf. mehrere Server gestartet werden, auch wenn das heute durch Container und Virtualisierungslösungen einfacher geworden ist, man muss sich im administrativen um eine ganze Menge mehr Maschinen kümmern.

Die nächste Stelle an der man mehr Aufwand hat, ist der Aufruf und die Kodierung der Daten. Hier handelt man sich bei Netwerkaufrufen gleich einmal den vollständigen TCP/IP-Stack ein, mit allen seinen sieben Schichten. Das mag nicht übermäßig viel sein, aber es kommt definitiv etwas Latenz hinzu – wenn der entfernte Server dann noch weiter weg steht und womöglich etwas knapp angebunden ist, merkt man das sehr schnell. Besonders heftig ist es, wenn man dann noch (ohne es zu wissen oder zu wollen) eine ganze Kette solcher Services lostritt, weil der abgefragte Server wiederum andere Services anstößt…

Ein weiteres oftmals vernachlässigtes Problem ist die Datenkonsistenz und Datenaggregation: Liegen die Daten alle in einer Datenbank gehört es zum guten Ton Fremdschlüssel zu verwenden und so etwaige Inkonsistenzen zu vermeiden. So kann man beispielsweise sicherstellen, dass mit jeder Veränderung eines Datensatzes der aktuell angemeldete Benutzer mitgeschrieben wird, und dass dieser gültig ist (also zum Beispiel seine ID in der Benutzertabelle existiert). Ändert sich nun der Benutzername, ist das alles kein Drama, die Datenbank bleibt in sich konsistent. Das Löschen einen Benutzers mit noch vorhandenen Datensätzen lässt sich ebenfalls unterbinden. Will man etwas in dieser Form mit getrennten Services realisieren hat man sehr schnell eine Synchronisationshölle geschaffen. Im besten Fall kann man noch eine ID des Benutzers oder dessen Login-Namen als String speichern. Wird dieser dann auf dem Authentifikationsserver gelöscht, bekommt die eigene Anwendung davon gar nichts mit. Der Benutzer kann sich zwar nicht mehr anmelden, aber die Tatsache, dass dieser Datensatz gelöscht wurde (was ggf. für die Applikation sehr interessant sein könnte) wird nicht weiter propagiert. Besonders heftig trifft es wenn Veränderungen am Login-Namen vorgenommen werden. Das passiert häufiger als man denkt: Ein Benutzer heiratet und schwupps ändert sich in vielen Firmen der Login, da er an den Nachnamen gekoppelt ist. Für das aufrufende System ist es ein neuer Benutzer. Der Benutzer kann seine Datensätze vom Vortag nicht mehr editieren, weil er ist ja nicht der Autor. Die Szenarien in dieser Hinsicht sind vielfältigst. Will man diesen Problemen Herr werden benötigt man wieder zusätzliche Services die einen Rückschluss auf die Veränderung zulassen (also z.B. die Frage: Wurde Benutzer x irgerndwann einmal umbenannt?)

Der Weg zu einer “hippen” Microservice-Architektur will also gut überlegt sein. Es kommt sehr auf den Anwendungsfall an und ob es mir im gegebenen Kontext überhaupt möglich ist einzelne Services so heraus zu lösen, dass sie vollständig eigenständig sind. Praktischerweise ist die Architekturwelt an dieser Stelle nicht vollständig digital bzw. schwarz/weiß, es gibt beliebig viele Abstufungen die man wählen kann und die alle legitim sind. Weder ist ein monolithische Architektur per se falsch und böse, noch sind Microservices das allein seelig machende Allheilmittel. Es hilft, sich über die Rahmenbedingungen im Klaren zu sein:

  • Habe ich tatsächlich unabhängige fachliche Teilbereiche?
  • Wie groß ist die Entwickler-Gruppe – lohnt es sich mehrere Teams zu bilden?
  • Gibt es eine hohe Abhängigkeit von Daten zwischen Anwendungen / Prozessen?

Man kann noch eine Reihe weiterer Fragen stellen um ein genaueres Bild zu erhalten.

Zum Abschluss noch ein kurzer Blick zurück in die Geschichte bzw. ältere Software-Architekturen. Modularisierung von Software gab es ja bereits schon früher. Dort hat sich der Ansatz “high cohesion, loose coupeling” bewährt. Wenn man es genau betrachtet, sind Mikroservices eine logische Weiterentwicklung dieses Ansatzes. Innerhalb eines Sources-Code-Repositories kann man diese Vorgabe nur als Regel für die Entwickler vorgeben: Es ist zu vermeiden, dass man von jeder beliebigen Stelle im Code andere Funktionen aufruft. Der Ansatz der Objektorientierung hat hier bereits wichtige moderierende Funktion. Ebenso kann man durch den Einsatz von Modulen das Bewusstsein schärfen, wenn ein (fachlicher) Bereich verlassen wird. Microservices schließlich erzwingen das Konzept durch weitere Hürden, wie das Verpacken in universelle Pakete (JSON, SOAP siehe auch oben). Ob derartige Zwänge immer zu besserer Software führen ist jedoch fraglich. Zudem gibt es in jedem Projekt doch immer wieder Funktionen die gleichartig sind – mit Mikroservices nimmt man bewusst in Kauf, dass ggf. Funktionalität zweimal ausprogrammiert wird. Ob das dann gute Architektur ist, darf sich jeder selbst überlegen.

 

 

DeveloperCamp 2016 Würzburg

Den Horizont erweitern, einfach auch einmal über den Tellerrand hinaus blicken und noch dazu jede Menge coole Leute mit Ähnlichen Interessen treffen. All diese Erwartungen wurden für mich durch das Developer Camp 2016 in Würzburg erfüllt.

Die Veranstaltung ging über zwei Tage Freitag und Samstag, wobei mein Arbeitgeber so freundlich war die Kosten für diese ungewöhnliche und erstmalig stattfindende Fortbildung und die Übernachtung zu übernehmen. Für die ein oder andere aktuelle Problemstellung gab es dann auch jede Menge Hinweise und Ideen zur Lösung. Details folgen weiter unten.

Um nichts zu verpassen bin ich mit meinem Kollegen Sebastian sehr früh in Mannheim aufgebrochen auch weil die Strecke nach Würzburg voller Baustellen und Staugefahren ist – Ergebnis: wir waren etwas vor der Zeit am Veranstaltungsort, aber es hat sich gleich die lockere Atmosphäre und die professionelle Organisation des Camps gezeigt – man war bereits auf Frühankommer eingestellt – inklusive “Laufzettel mit Gewinnspiel” und dem wichtigsten für den Informatiker: frischer Kaffee.

Continue reading

MySQL und Barrakudas

Eigentlich sollte man sich um einen Datenbankserver möglichst wenig kümmern müssen. Regelmäßige Updates zur Sicherheit sind auch kein Thema. Bei der Konfiguration sollte man in der Regel auch nicht alle Tage was neues ausprobieren.

Spannend wird das nur, wenn dann etwas nicht funktioniert und man auf die Suche geht. So etwas ist mir heute einmal wieder passiert. Auslöser war eigentlich eine harmlose Sache: Vor kurzem gab es eine Fehlermeldung, dass ein Datensatz eines Benutzers nicht gespeichert werden konnte – mit dem Hinweis:

MySQL Error 1118 (Row size too large)

Soweit so ärgerlich – ich habe da erst ein wenig gesucht, aber nichts gefunden – da der Fehler sich auch irgendwie nicht adhoc reproduzieren lies habe ich mich erst mal um das Tagesgeschäft gekümmert. Bis mir das gleiche Problem heute selbst auf die Füße gefallen ist, allerdings nicht an ursprünglicher Stelle sondern bei einer ganz anderen Aktion: Eigentlich wollte ich nur “mal eben schnell” feststellen ob die Performanceprobleme einer neuen Funktion nur darauf zurück zu führen sind, dass unser Entwicklungssystem schon etwas älter ist und damit nicht immer gerade als Vergleich herhalten kann (es hat gewisse Vorteile ein solches System zu verwenden: Was da schon nicht prickelnd läuft, ist in der Regel ein Hinweis, dass man sich die Programmierung nochmal anschauen sollte).

Nunja in diesem Fall hat die Analyse der Programmierung leider nicht so recht gefruchtet – daher wollte ich einen Versuchsballon starten und die aktuelle Entwicklung auf unserem Produktiv-System in einer Spielwiese testen. Normalerweise ist das kein Problem und wenn die Performance dort dank mehr Rechenleistung ausreichend ist, kann man es ja erst mal so lassen. Ich nehme also meinen Entwicklungs-Dump und versuche diesen in die Spielwiese zu laden. Aber – oh Wunder – ich renne genau in den Fehler von oben hinein. Allerdings mit einem Dump, den ich aus einer bestehenden Datenbank extrahiert habe – die Größe müsste also ja passen, denn sonst wäre es ja gar nicht erst reingekommen.

Aber der Reihe nach: Ich suche natürlich erst einmal nach einem Problem mit der Größe, dabei stoße ich darauf, dass es eine Veränderung an der Art und Weise gab, wie die Datenbank mit ihrer InnoDB-Engine die Daten ablegt – das neue Format wurde “Barracuda” getauft. Dieses soll unter anderem effizienter mit dem Speicher haushalten, bzw. man kann festlegen wie ggf. gespeichert werden soll. Generell gilt für die InnoDB: Nur große Objekte (also TEXT, BLOB und Consorten) werden außerhalb der Seite abgelegt, alles andere bleibt in der Page. Das kann zu dem oben genannten Fehler führen. Allerdings muss man es da schon recht dicke treiben, denn die Blattgröße ist maximal 8126 Bytes bezüglich der Daten. Selbst wenn ich nun vom absoluten Worst-Case für meine Datenbank ausgehe und für jedes VARCHAR-Feld pro Zeichen von maximal für Bytes (weil wir mit UTF-8 arbeiten) ausgehe, komme ich auf etwas mehr als 600 Bytes. Also kann das nicht der Grund sein… ebenfalls würde das noch nicht erklären, warum es mit dem früheren Speicherformat (names “Antelope”) bisher funktioniert hat.

Auch wenn es auf den ersten Moment komisch erscheint, habe ich dann im Testsystem mal nach einem weiteren StackExchange-Eintrag experimentiert, und ins Schwarze getroffen. Mit der veränderten Einstellung lief der Import plötzlich sauber durch. Ganz wichtig dabei ist, dass man es wie hier beschrieben macht. Also erst den Server-Prozess sauber beenden, damit wirklich alle Änderungen aus dem Log geschrieben sind. Dann zusätzlich die Index, Log und Cache-Files für die InnoDB aus dem Weg räumen, dann umstellen und den Serverprozess wieder starten. Der kümmert sich dann darum, dass die notwendigen Verwaltungs-Dateien automatisch wieder erzeugt werden und diesmal die neue Größe haben.

Problem gelöst, nur weshalb trat der Fehler eigentlich auf? Denn man möchte ja etwas dabei lernen. Die Ursache ist in der Art und Weise zu suchen, wie der MySQL-Dump funktioniert: Im Prinzip ist die Datei nur eine große Sammlung von Statements, die nacheinander ausgeführt werden. Dabei wird natürlich auch Rücksicht auf Abhängigkeiten genommen, z.B. die Fremdschlüssel – denn es kann ja passieren, dass Tabelle “A” auf eine Spalte in der Tabelle “Z” referenziert, die Tabelle “Z” aber noch nicht erzeugt wurde, wenn “A” rekonstruiert wird. Die Daten werden dabei (wie man es auch ganz regulär machen würde) als Insert-Statements in die Tabellen geschrieben. InnoDB beachtet das ACID-Prinzip, jedes Statement wird daher als atomare Operation betrachtet. Um effizienter zu arbeiten, verwendet der Dump folgendes Konstrukt

insert into table a values ('a','b','c'), ('d','e','f'), .... ('xyz','aa','cc');

Das ist völlig legitim. Man kann alternativ auch folgende Statements ausführen.

insert into table a values ('a','b','c');
insert into table a values ('d','e','f');
....
insert into table a values ('xyz','aa','cc');

Das Ergebnis ist das Gleiche. Nur intern ist es ein kleiner aber feiner Unterschied. Um die ACID-Kriterien zu erfüllen, wird im zweiten Fall nach jedem einzelnen Statement ein Commit durchgeführt (was etwas Performance kostet), für InnoDB ein Zeichen, dass man die Log-Datei “wegschreiben” kann und damit dann auch wieder leeren kann, es ist also Platz für neue Daten in der Datei vorhanden. Passiert das nicht (wie im ersten Fall, in dem InnoDB davon ausgehen muss, es handelt sich um ein atomares Statement) kann die Log-Datei zu klein sein, was dann zur Fehlermeldung führt. Warum dann aber eine völlig fehlweisende Fehlermeldung auftaucht, die keinen Hinweis auf das Logfile enthält, bleibt vorerst noch Sache der jeweiligen Entwickler. Ich habe mal einen Bug-Report ausgefüllt, mal sehen ob das behoben werden kann (oder ob ich selbst einmal die Zeit finde mich dem Problem zu widmen).

 

 

Weihnachtsbastelei – Treiber für ein ASUS Pro70U Laptop

Alle Jahre wieder ist nach Weihnachten Chaos – auch bei diversen Freunden und Verwandten. Eigentlich mache ich das ja ungern während der Feiertage, aber wenn man als Experte schon mal vor Ort ist, dann packt man irgendwann dann doch an – auch wenn man nicht unbedingt Lust hat. Sollte ja auch nur eine Kleinigkeit sein, der Rechner an sich läuft ja schon, nur die Webcam mag nicht…

Gesagt getan, man schaut sich das Ding einmal an: Ein Asus Laptop Modell Pro70U – na das sollte doch eine machbare Größe sein. Sollte … Zu dem Gerät findet man bei Asus schon einmal keine Information, was irgendwie verdächtig ist. Wie ich dann heraus gefunden habe, ist das ein Gerät was ursprünglich einmal in den Niederlanden verkauft wurde – aber auch auf der niederländischen Seite von Asus findet sich nichts wirklich aussagekräftiges. Also nichts mit mal eben schnell.

Das Gerät ist schon durch mehrere Hände gegangen – es hat also auch schon ein paar Jahre auf dem Buckel. Das merkt man auch am Prozessor und an den Treibern – ein Athlon X2 als Prozessor ist noch dazu nicht gerade ein gängiges Modell geworden. Wenn man die Performance anschaut die das Gerät hinlegt, wundert einen das auch nicht mehr.

Mit etwas Glück habe ich dann herausgefunden, dass es sich wohl um ein umgelabeltes Gerät handelt was mal als Asus A7U verkauft wurde. Ich vermute, dass es sich ein Gerät handelt, das bei einem Discounter verkauft wurde, dort zwar als Asus beworben werden sollte, aber man nicht offen eingestehen wollte, was für ein Gerät man da eigentlich verkauft. Die Webcam ist dann auch nicht gleich installierbar – denn es gibt wieder einmal mehrere Sub-Modelle des Geräts, die unterschiedliche Peripherie installiert haben. Mit dem letzten Treiber funktioniert das Gerät dann auch endlich. Was für ein Krampf – aber: Andere sitzen nach Weihnachten über ihren Bausätzen und Modellen – der typische Nerd bastelt eben an unwilligen Rechner – und man braucht doch auch immer mal wieder eine Herausforderung.

Nur noch eine Kleinigkeit zusätzlich, um die man mich bittet: Da man leider nicht 10-Finger schreiben kann – der Aufdruck auf der Tastatur passt leider nicht zum gewählten Layout im Windows-Betriebssystem. Also nochmal raten was das genau ist – in meinem Fall sind wir am Ende mit einer amerikanischen Tastaturbelegung erfolgreich.

Jetzt bleibt nur zu hoffen, dass ich das Gerät nicht so schnell wieder in den Fingern habe, denn das eben schnell mal einen Treiber installieren hat dann doch einige Stunden gedauert. Das liegt auch daran, dass das Gerät leider nicht wirklich eine ansprechende Performance abliefert – zumindest nicht unter Windows 7. Zwischenzeitlich war ich derart frustriert, dass ich beinahe zum USB-Stick mit einem Linux gegriffen hätte.