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.

IPv6 – das „neue“ Internet-Protokoll und der steinige Weg dorthin

Nun habe ich am 22. und 23.05.2014 die Möglichkeit bekommen am IPv6-Kongress in Frankfurt teilzunehmen. So langsam aber sicher wird es Zeit sich um das neue Internet-Protokoll zu kümmern und damit vertraut zu werden. In erster Linie ist es natürlich ein berufliches Interesse, hier am Ball zu bleiben. Aber auch für mich privat als „digital Native“ bis „Nerd“ – irgendwo dazwischen liege ich wohl – ist es natürlich interesant zu wissen was als nächstes kommt.

Nun ersteinmal: Warum das ganze? Nun, das Internet ist langsam aber sicher gewachsen, schneller als man es ursprünglich einmal gedacht hat und noch dazu in Ausdehnungen die sich früher kein Mensch so recht vorstellen konnte. Wichtigstes Prinzip war immer die sogenannte Ende-zu-Ende Kommunikation nach dem Best-Effort-Prinzip: Jeder leitet alle Daten zielführend und so gut es geht weiter, die kleinste Einheit dafür sind sogenannte Pakete. Egal was man an Daten im Internet verschicken möchte, alles wird in kleine Häppchen zerlegt und vom Sender zum Empfänger transportiert. Dabei ist es (fast) unerheblich was transportiert wird: Sprache, Text, Bilder, Videos, Dateien etc.: Alles wird auf die Pakete verteilt – man kann es wie das Versenden einer umfangreichen Lieferung sehen: Was auf einen LKW passt wird auf einen geladen und verschickt, wenn es mehr wird, teilt man es auf. Vorteil: Wenn mal tatsächlich etwas nicht ankommt, muss man nur den fehlenden Teil der Daten nachschicken (da wird es dann mit den LKWs ggf. etwas schwierig).
Was braucht man um Pakete zu verschicken? Richtig, eine Adresse damit der Postbote oder Paketdienst weiß wohin mit der Lieferung. Dazu wurde das Internet-Protokoll entwickelt, kurz IP. Derzeit werden Adressen mit 32 bit Länge verwendet, geschrieben in 4 Blöcken zu je 8 Bit. Das gibt dann Angaben wie 192.168.10.74. Diese Adressen werden gemäß ihrer Historie als IPv4 (vierte Revision des Protokolls in Verbindung mit TCP) bezeichnet.

Nun funktioniert das ja momentan schon, weshalb also ein neues Protokoll einführen? Never touch a running system bzw. never change a winning team…. Nun es gibt gute Gründe dafür, denn das Netz ist seit der letzten Protokollversion immens gewachsen. Niemand hätte anfänglich davon zu träumen gewagt, dass IP derartig populär und weltweit im Einsatz sein würde. Auch waren 32 Bit zu dem Zeitpunkt ein nahezu unvorstellbar großer Zahlenbereich.
Heute hat fast jeder mindestens einen PC, Laptop und ein Smartphone ggf. noch weitere Geräte wie Netzwerkdrucker, IP-Telephone, IP-Webcams, Internetfähige Fernseher etc. Für jedes Gerät wird eine eigene Adresse benötigt… Wenn jeder Mensch auf der Erde ein Gerät hat, dann reicht es schon nicht mehr…
Für den Gebrauch in abgeschlossenen Netzwerken gibt es Adressen, die nicht weltweit eindeutig sind – so lange die Netzwerke nicht miteiander kommunizieren müssen ist das auch legitim. Die bekannstesten Adressen für diesen Fall sind die der Class C: 192.168.x.y, für größere gibt es die Class b (172.16.0.0 bis 172.32.255.255), in großen Unternehmen gibt es meist Class A (10.x.y.z).
Schon früh hat man Methoden entwickelt um Internet-Anschlüsse für mehrere Rechner auf einmal nutzbar zu machen. Einige alte Hasen erinnern sich unter Linux noch an ipfwadm, später ipchains und heute iptables. Die Werkzeuge können noch viel mehr aber eben auch IP Masquerading bzw. Network-Address-Translation (NAT). Noch heute kommen genau die gleichen Mechanismen zum Einsatz, meist hübsch verpackt in einem Multifunktionsgerät das viele als Router bezeichnen (was technisch nur teilweise korrekt ist). Netter Nebeneffekt: ohne besondere Einstellungen (z.B. Portweiterleitungen) sind die Geräte im Netz auch erst mal vor Zugriffen von außen geschützt.
Im öffentlichen Internet dürfen die oben genannten Bereiche nicht verwendet werden, sie fallen (wie einige andrere Bereiche mit speziellen Funktionen) aus den zur Auswahl stehenden IP-Adressen heraus. Es wird also noch knapper.
Nun kann man ja sagen: was einmal klappt, geht auch zweimal… Also schachteln wir doch ein Netz mittels IP Masquerading in ein anderes. Das funktioniert (leider) recht gut. Zudem erhöht es die Sicherheit, sollte man meinen. Schwierig wird es dann aber Geräte in der zweiten Ebene anzusprechen: am eigenen Router kann man noch Portweiterleitungen einrichten, ein Provider mit mehreren tausend kunden kann das nicht. Insgesamt wird also das Ende zu Ende Prinzip ausgehebelt. Im kleinen ist das ja noch ok, aber weltweit wird es schwierig… Denn ein Fernzugriff aus die zentrale Dateiablage daheim ist sehr praktisch (dann muss man die Dateien nicht irgendwelchen Clouddiensten anvertrauen). Genau das klappt dann aber eben nicht mehr.
Es gibt noch einige gute Gründe mehr weshalb IPv4 ersetzungsreif ist, aber das würde den Rahmen hier sprengen.

Nun gut, ich bin technisch ja durchaus interessiert, also würde ich IPv6 dann auch gerne baldest möglich nutzen. Zumal ein Parallelbetrieb ja sogar explizit vorgesehen und möglich ist. Das brauche ich beispielsweise für meinen gut gedienten Netzwerkdrucker, für diesen wird es keine Softwareaktualisierung geben, die ihn  IPv6 fähig macht. Aber der Rest meines Geräteparks ist soweit eigentlich fit. Bis auf … wer hätte es gedacht? Den Router. Nun gut, der hat auch schon einige Jahre auf dem Buckel und es gab ihn bereits bei Vertragsabschluss als Endgerät.

Also frage ich erst mal bei meinem Provider nach, wie es denn aussieht mit IPv6 und vor allem ob es eine Möglichkeit wieder ein subventioniertes Gerät zu erhalten. Fragen kostet nichts und man kann nur Erkenntnis gewinnen. Prompt habe ich die Antwort, das er Ersatz des Routers wahrscheinlich möglich sei, aber ich möge doch bitte nochmal in der Technik anrufen. Gesagt getan. Dort verweißt man mich dann aber doch in die Vetragsbetreuung. Dort heißt es dann, dass ein Upgrade des Routers nur gegen Zuzahlung von 90 EUR möglich ist, was mir für das kleinste Gerät mit Branding dann doch etwas viel Geld ist. Ich lehne das Angebot also erst einmal ab und schaue mich derweil bei den Gerbauchtgeräten um.

Am nächsten Tag erfolgt ein weiterer Anruf, man signalisiert beim Router Entgegenkommen und senkt die Hürde auf 49 EUR – immer noch zu viel wie ich finde – die Geräte gehen bei ebay für um die 35 EUR über den virtuellen Tresen und Versandkosten hätte ich in beiden Fällen. Also ordere ich erst mal eine FritzBox mit IPv6-Fähigkeit – immer in dem Glauben, dass dies das Tor zur IPv6-Welt aufstoßen wird.

Einige Tage vergehen, ich bin auch sonst gut ausgelastet, aber einige Wochen danach komme ich dann endlich dazu die Box anzuschließen. Erste Ernüchterung bei den Fähigkeiten: Bisher gab es zwei normale analaoge Anschlüsse intern, jetzt nur noch einen, der andere ist für ein Fax reserviert (TAE-N-Anschluss) – die Software lässt sich auch nicht überreden den Anschluss zweckentfremdet zu nutzen. Das ist ärgerlich, denn mittlerweile haben wir ja zwei Telefone die wir auch beide parallel nutzen möchten und bisher konnten. Aber egal: Jetzt will ich erst einmal wissen wie es um IPv6 steht und für einen Test ist das Setup ja durchaus geeignet.

Doch was muss ich feststellen: Ich bekomme keine IPv6-Adresse bzw. ein entsprechendes Präfix zugeteilt. Das ist ein Problem des Providers, weshalb ich den wieder kontaktiere. Die Antwort ist diesmal ernüchternd: IPv6 gibt es nur für einige Kunden mit V-DSL und in einem Pilotprojekt. Ein allgemeiner Start ist noch nicht in Aussicht.

Also wird es erst mal nichts mit IPv6 bei mir. Auf eine Lösung mit Tunnel möchte ich eigentlich ungern setzen, die Option Dualstack-Lite wäre für mich ja auch noch OK gewesen. Was bleibt ist ein schaler Beigeschmack über das Verhalten meines Providers bezüglich des Routers – wollte man mir hier einfach nur ein Neugerät verkaufen oder ist das nur ein Kommunikationsproblem gewesen. Ersteres fände ich reichlich dreist, letzteres ist wohl aber in modernen Unternehmen immer wieder der Fall. Warten wir also ab, was kommen wird – vielleicht auch irgendwann IPv6.

 

Modularer Querbaukasten in PHP: Traits

Objektorientierte Entwicklung ist mittlerweile der Standard wenn es um Geschäftslogiken geht. Für verschiedene weitere Anwendungszwecke gibt es auch immer noch gute Funktionale Programmiersprachen. Jede Herangehensweise hat ihre Vor- und Nachteile.

Wie immer sind Programmierer komische Wesen und würden an einigen Stellen gerne das Beste aus beiden Welten haben, auch wenn diese sich eigentlich gegenseitig ausschließen.

Aktuell hatte ich mal wieder einen solchen Fall: Es gibt eine „fertige“ PHP-Bibliothek zum Erzeugen von PDF-Dokumenten (diese eigenen sich in der Regel besser zum Ausdrucken als HTML-Seiten) – etabliert hat sich hier FPDF bzw. dessen Fork TCPDF. TCPDF ist die aktuellere und modernere Fassung, sie unterstützt unter anderem die Verwendung von UTF-8-Zeichensätzen. Daher musste ich mich auch „mal wieder“ mit schon lange gepflegten und eigentlich abgeschlossenen PDF-Routinen beschäftigen: FPDF kann mit UTF8 nicht richtig umgehen, somit sehen die erzeugten PDFs aus wie Kraut und Rüben sobald Umlaute oder Sonderzeichen im Spiel sind.

Beim Umstellen gibt es ein paar Feinheiten, aber nichts was einen wirklich davon abhält die Umstellung halbwegs zügig durchzuführen. Die meisten Probleme erkennt man einmal und kann sie dann in den Folgedateien direkt korrigieren.

In diesem Zusammenhang habe ich mich mal wieder etwas geärgert: Viele unserer Dokumente enthalten Tabellen. Soweit so langweilig. Aus Mangel an Alternativen haben wir häufig den Code einfach von der einen eigenen Klasse in die nächste mit kopiert. Das Ergebnis ist altbekannt. Wenn man einen Fehler beseitigen will, muss man alle Kopien mit durcharbeiten. Effizient ist das nicht gerade.

Eine mögliche Option ist es eine eigene Klasse dazwischen zu setzen, die solche Funktionen realisiert. Aber was ist mit den Dokumenten wo man dann gar keine Tabelle braucht – da hat man Pech gehabt? Fazit das geht, aber richtig schön finde ich das auch nicht. Mehrfach-Vererbung kennt PHP nicht – aus gutem Grund (Diamond-Problem). Für meinen Fall hätte ich mir etwas gewünscht wie eine Art Plugin oder von mir aus auch eine Art „include“ das entsprechende Funktionen einbindet wenn sie benötigt werden. Vergleichbar mit dem was ein Präprozessort für C erreichen kann: Macro-mäßig das einfügen was gerade gebraucht wird – bei compilierten Sprachen eine sehr feine Sache, bei Skripten leider kein gangbarer Weg.

Was braucht man eigentlich genau? bzw. was wünsche ich mir? – Das waren meine Fragen. Damit man eine Tabelle einfach einbauen kann braucht man Zugriff auf einige Klassenvariablen – man könnte sich ggf. behelfen, eine Referenz an eine Tabellenklasse zu übergeben und dort den notwendigen Code zu schreiben. Aber eine Tabelle ist ja auch nur wieder eine Sammlung von einfacheren PDF-Elementen – würde also auch wieder aus der TCPPDF-Klasse erben müssen, davon den Code in die andere Klasse zu übergeben mal ganz abgesehen. Eine Art „Nachbau“ des Präprozessors wäre es wenn man hergehen könnte und an der benötigten Stelle in der Klasse einfach per include die Funktionen einbaut die man braucht. Das geht natürlich so nicht, auch weil es im Tabellencode ja durchaus sinnvoll ist, diesen strukturiert zu halten und keinen Spaghetti-Code zu erzeugen, den keiner mehr durchschaut. Auch die Nutzung von Klassenvariablen innerhalb der Tabelle wäre doch praktisch. Kurzum: ein simples Include kann das nicht leisten und sollte es auch nie.

Nun ist es ja aber bei weitem kein super exotisches Problem, mit dem wir hier konfrontiert sind: Modulbauweise mit Blackboxes die nur definierte Schnittstellen haben ist ja auch in anderen Bereichen üblich: Keiner entwickelt heute mehr für jedes Auto Standard-Komponenten vollständig neu – vielmehr werden Grundelemente mit einen neuen Aufsatz versehen oder neu miteinander kombiniert. PHP löst das Dilemma mit sogenannten Traits – ich vergleiche das gerne mit Matrix-Vererbung – um ein Gesamtpaket zu erhalten markiert man einfach die Zusatzfunktionen die das Ergebnis haben soll. Klingt kompliziert – ist es aber nicht.

Einen Trait programmiert man fast wie eine ganz normale Klasse – abgesehen, davon dass sie keinen eigenen Konstruktor enthält, gibt es alles was man sonst auch kennt: Funktionen, Zugriffsbeschränkungen (public,private,protected) und Klassenvariablen. Kurzum man kann alles was man immer wieder braucht damit sauber realisieren – in meinem Fall, habe ich alles was ich für eine Tabelle benötige im Trait verpackt. Man hat sogar Zugriff auf die Methoden und Variablen der Klasse in der man den Trait am Ende einsetzen will – hier ist die Unterstützung der Entwicklungsumgebung natürlich nicht so gut wie in einer vererbten Klasse, denn der Trait kann ja in jeder beliebigen Klasse verwendet werden – von daher etwas mehr Denkarbeit. Ich habe vieles direkt aus der bisherigen Realisierung übernehmen können, das macht es etwas leichter.

Was man noch beachten muss: Gleichnamige Funktionen in der übernehmenden Klasse „überdecken“ bzw. überschreiben die Funktionen des Traits (vergleichbar mit dem was man aus der Vererbung kennt: Es wird immer die „nächstliegende“ Funktion genommen, es sei denn man gibt den Namespace bzw. die Elternklasse mit an (parrent::) . Für mich hat es sich bewährt im Trait die Funktionen als solche entsprechend zu bennen – sie bekommen alle den Prefix „pdf_table_trait_“ – die Wahrscheinlichkeit, dass man somit ungeplant etwas überschreibt ist vergleichsweise gering. Die Programmiersprache nimmt einem eben nicht alles ab und etwas Coding-Disziplin hat noch keinem Entwickler geschadet.

Insgesamt habe ich nun mit dem Trait das was ich gesucht habe: Einen Code-Block, den ich separat warten kann – wenn ich einen Fehler in der Tabellenerzeugung beseitige, dann muss ich das nur noch an einer Stelle tun, nicht mehr in jedem Dokument. Zudem habe ich es so realisiert, dass ich den Code sogar innerhalb einer Klasse mehrfach aufrufen kann um verschiedene Tabellen zu zeichnen (das hatte ich vorher mit etwas Verrenkungen unsauber gelöst). Man sollte natürlich jetzt nicht alles und jedes zwingend in einen Trait auslagern, nur weil es geht – mit Maß und Ziel das richitige Werkzeug an der richtigen Stelle ansetzen. Traits gehören dabei eindeutig in die Kiste mit der Aufschrift „Spezialwerkzeuge“ – man braucht sie nicht all zu häufig, aber wenn man weiß dass es sie gibt und wie man sie richtig einsetzt, geht vieles sehr elegant zu lösen.

Vom Himmel hoch da komm ich her – Skydrive-Schwachsinn zur Weihnachtszeit

Vor einigen Tagen hatte ich mal wieder ein absolutes Schlüsselerlebnis, das mich in meiner Skepsis gegenüber Windows 8 und Microsoft-Produktem im Allgemeinen deutlich bestärkt hat. Sicher sind die Produkte von Microsoft in vielen Fällen ein gewisser Standard, aber es gibt immer wieder Ecken und Kanten an denen ich mich einfach nur ärgere wie etwas gelöst ist.

Windows Vista ist ebenso eine Ladenhüter wie es schon seinerseits Windows ME war – viele Feautres gut gemeint und gewollt, aber die Realisierung war nicht der Knaller. Ähnliches sehe ich gerade bei Windows 8 – auch hier hakt es an vielen Stellen und das Betriebsystem hat sich meilenweit von dem entfernt was es eigentlich machen sollte – Ressourcen des Rechners bündeln und allen Anwendungen zur Verfügung stellen, auf Basis einer einheitlichen Schnittstelle. Das Fehlen des Startmenüs mit der Suchfunktion ist eines, aber eine komplette Touch-Bedienung für einen Desktop-PC oder Laptop an dem Maus und Tastatur angeschlossen sind – das kann in meinen Augen nicht hinhauen. Ironie dabei ist der Werbesong „Everything at once“ von „Lenka“ – dort werden verschiedenste nicht miteinander vereinbare Gegensätze postuliert, welche die Sängerin alle doch erfüllen möchte. Wer das mal probiert hat, weiß: Manche Dinge schließen sich einfach gegenseitig aus … Ich bin gespannt wann sich diese Erkenntnis auch in Firmen herum spricht (und das betrifft leider nicht nur Microsoft, auch andere Firmen verwenden „Standard-Software“ für Zwecke, für die sie nie gedacht war und wundern sich dann noch über „komische“ Ergebnisse).

Was war das konkrete Problem diesmal? Ein Kunde wollte wie bisher unter Windows verschiedene Bild-Dateien per e-mail verschicken. Soweit nichts wirklich Spannendes oder Nerviges. Da es bequem und auch legitim ist, markierte er die Bilder seiner Wahl und wählte im Kontext-Menü: Senden an … => e-mail-Empfänger. Nun ging auch brav das eingerichtete (und mitgelieferte) Windows Live Mail auf (oder wie ich es nach der Erfahrung wohl eher nenne: „Windows Live Quäl“ – es quält dich ggf. ein Leben lang…). Interessanterweise wollte das System jedesmal einen Hotmail-Account einrichten, obwohl bereits eine e-mail-Adresse konfiguriert war. Klickte man den Dialog einfach weg und schaute in die e-mail so wurde vom Mailprogramm angestrebt, die Bilder nur als Thumbnails in die e-mail einzubetten und die Originale zu Skydrive für eine bestimmte Zeit hochzuladen und per Link oder Bildergalerie verfügbar zu machen. Natürlich ist das nicht das was der Anwender eigentlich wollte, nämlich die Bilder einfach an die e-mail anhängen.

Was ist der Hintergrund? Generell bietet e-mail die Möglichkeit Anhänge mit zu versenden, so wie man es auch von klassicher Snail-Mail her kennt. Einfach etwas mehr in den Umschlag packen und im Anschreiben darauf verweisen. Mit HTML-Mails sind auch direkte Anzeige-Referenzen möglich, aber im Prinzip ist es jedesmal ein elektronisches Kuvert mit dem Inhalt, mal nur reiner Text, mal eben mit Anlagen. Problematisch ist die Mailbox-Größe der Empfänger bzw. Restriktionen der e-mail-Server aus Performance und Sicherheitsgründen: Oftmals sind hier Limits für eine Nachricht vorhanden und oft sind die recht knapp bemessen (es sei denn man ist sein eigener Mail-Admin und stellt sich die Einschränkungen entsprechend ab oder zumindest mal hoch ein). Daher ist es bei größeren Dateien oder Datenmenge oftmals ratsam auf Alternativen auszuweichen, also zum Beispiel den Upload der Datei auf einen Webserver und dem Empfänger nur einen Link (genauer einen URL) zuzuschicken damit er die Datei bei Bedarf herunter laden kann. In Etwa kann man es mit dem Angebot eines Webshops vergleichen bei dem man online bestellt und dann die Info bekommt: „Ihre Ware können Sie in Warenhaus XYZ in ihrer Nähe abholen, sie ist dort für sie bis zum soundsovielten reserviert).

Daher ist das Ansinnen von Live-Mail ja ganz gut gemeint, bewahrt es doch den Nutzer vor etwaigen e-mail-Rückläufern wenn die e-mail nicht zugestellt werden kann. Aber will ich wirklich jedes Bild in die Cloud eines amerikanischen Konzerns auslagern? Gerade im Blick auf die NSA-Affaire halte ich den Vorschlag für nicht zielführend und gelinde gesagt für Irreführung des Benutzers. Es wird eine Notwendigkeit vorgegaukelt die es schlichtweg nicht gibt.

Ich selbst verwende schon seit sehr langer Zeit alternative e-mail-Programme wie Pegasus-Mail (früher) und Mozilla Thunderbird (aktuell). Thunderbird macht hierbei vor wie man es besser machen kann: Hängt man Attachments einer bestimmten Größe an, gibt es einen Hinweis auf einen Cloudservice, der darauf hinweist, dass es problematisch sein könnte eine derat „dicke“ e-mail zu versenden, und ob man diesen nutzen möchte. Man hat also definitiv die Wahl was man machen möchte, etwas was mit Live Mail leider nicht geht – zumindest habe ich auf die Schnelle keine passende Option gefunden und diverse Foren-Einträge im Netz schildern ähnliche Erfahrungen. Von daher kann ich leider als ITler keinem Menschen mehr die Benutzung von Live-Mail auch nur nahelegen als Alternative, sondern kann nur auf externe Programme verweisen die dem alten Unix/Linux-Mantra der Software treu geblieben sind „do one thing and do it right“ – auf gut deutsch: Mache eine Sache, die aber dafür richtig und nicht alles nur so halb und dafür auf einmal (everything at once). In der Linux-Welt gibt es viele spezialisierte Programme für genau einen Zweck, weitere Funktionen, die nichts mit der eigentlichen Aufgabe zu tun haben, sucht man aber (zu Recht) vergeblich. Sicherlich ist das nicht immer und überall bis ins Letzte sinnvoll, aber es hat sich als gute Richtlinie für die Modularisierung von Programmen erwiesen: Für jeden Zweck das passende Werkzeug. Man versucht doch auch nicht, Schrauben mit einem Hammer „einzuschrauben“…

Einen Würg-Around gibt es dann dennoch für Windows Live Mail und das Kontext-Menü:

  1. Im Ordner, in dem sich die Bilddateien befinden eine leere Textdatei erzeugen (z.B. bla.txt), diese muss noch nicht einmal Inhalt haben
  2. Zusätzlich zu den Bildern die gerade erzeugte Dummy-Datei anwählen
  3. Kontextmenü => Senden an e-mail-Empfänger
  4. In Windows Live-Mail wird nun eine e-mail mit regulären Attachments erzeugt (es geht also …)
  5. Aus der Attachmentliste und der Auflistung im Text die leere Textdatei wieder rauswerfen
  6. Voila!

Das so ein Murks notwendig ist, zeugt davon, dass man scheints noch nicht gelernt hat, auf einen Anwendungsfall wirklich passende Lösungen zu liefern – das Gängeln des Benutzers hat ja aber auch eine jahrzehnte lange Tradition bei Microsoft. Warum das wissen wohl nur die Marketing-Menschen aus Redmond.

In diesem Sinne erst mal frohe Weihnachten und viel Spaß beim Verschicken von e-mails mit handelsüblichen Attachments.