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.

 

Magento 2 – Rest-API oder Reste-API?

Beruflich darf ich mich gerade mit Magento als Shop-System auseinandersetzen. Auf den ersten Blick ein Shopsystem wie es viele auf PHP-Basis gibt. Jedes System hat eine andere Geschichte und damit seine Eigenheiten und besonderen Pluspunkte. Leider finde ich von den Pluspunkten bei Magento für meinen aktuellen Fall etwas zu wenig bis gar keine. Wir haben eine externe Datenquelle, welche verschiedene Verwaltungsabläufe abbildet, an deren Ende eine Reihe von Produkten für den Shop stehen.

Anfänglich war ich vielleicht etwas naiv und dachte: Ein paar Zeilen SQL und dann lässt sich das Datenmodell sicherlich so extrahieren, dass man recht schnell und zuverlässig die Daten vom Quellsystem in den Shop überführen kann. Leider ist das nicht der Fall, denn Magento hat sich als Datenmodellierung für EAV entschieden. Damit ist nicht die erste allgemeine Verunsicherung gemeint (auch wenn die dann nicht mehr weit her ist) sondern eine Modellierungstechnik namens Entity-Attribute-Value. Im ersten Moment klingt das Modell für einen Shop sogar sehr lohnenswert, denn eine Shopsoftware muss ja ggf. mit verschiedensten Waren zurecht kommen. Genau das ist mit EAV möglich: Man bildet die Modellierung von zusätzlichen Eigenschaften über weitere Tabellen ab. So vernünftig das anfänglich klingen mag, um so schwieriger wird die Umsetzung in der Praxis. Da man bestimmte Attribute ja auch in verschiedenen Fällen wieder brauchen kann hat man die noch eine Stufe weiter abstrahiert. Das Ergebnis ist ein sehr umfangreiches Modell, das Anna Völkl schön aufbereitet hat. Übersichtlich ist etwas anderes, auf diesem Datenmodell  manuell direkt auf Tabellenebene zuzugreifen kann man getrost vergessen.
Continue reading

Posted in Web |

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.

Routing mit HTTP – Query-String oder Verzeichnis-Notation?

In loser Folge werde ich in den nächsten Monaten einige Vorgehensweisen im Netz technisch etwas näher beleuchten und hinterfragen.

Jedes aktuelle Framework, das etwas auf sich hält im Bereich Webentwicklung, bringt einen Routing-Mechanismus mit. Was macht das Ding? Es kümmert sich darum, dass aus einer URL heraus die passende Applikation bzw. Teilapplikation aufgerufen wird. Wenn man also http://example.com/cart/list aufruft, dann wird der passende Programmcode ausgeführt, der sich um die Anzeige des Warenkorbs kümmert. Soweit so einfach. Innerhalb der Frameworks wird dann häufig per Model-View-Controler gearbeitet. Die Url wird dann noch weiter “genutzt” um den passenden Controller und die passende Action (sofern es verschiedene gibt) zu ermitteln. Mittlerweile auch gängige Praxis ist es alles was danach noch als scheinbare Verzeichnisstruktur kommt als Parameter zu betrachten, immer wechselweise Name/Wert.

Auf den ersten Blick sieht das ja schick aus, denn die URLs werden dadurch ggf. “sprechender” bzw. man kann sie sich leichter merken. Aber nicht alles was schön / schick aussieht ist auch technisch sinnvoll. Ich selbst reibe mich regelmäßig an dieser Form der Applikationssteuerung.

Am ärgerlichsten finde ich den Missbrauch der Verzeichnisstruktur zur Parameterübergabe. Hierfür hat das HTTP-Protokoll bzw. das generische URL-Schema nämlich seit den Kindertagen des Netzes eine passende Lösung. Diese nennt sich Query-String und ist an vielen Stellen des Netzes erfolgreich im Einsatz. Es handelt sich dabei um das “Anhängsel” an Internet-Adressen die nach einem Fragezeichen folgen: Also z.B. http://example.org/suche.html?was=einThema. Die Applikation kann den Querystring (im Code wird er häufig als “GET-Parameter” genannt) dann direkt auswerten. Auch für den Fall mehrerer Parameter ist vorgesorgt: Diese werden per Kaufmanns-Und (Ampersand) getrennt. Das sieht dann so aus: http://example.org/suche.html?was=einThema&wo=wohnort

Technisch steht dabei das Konzept des Parametrisierens von Programmen (und letztlich Funktionen) dahinter. Das Äquivalent aus der Vor-Web-Zeit (das auch heute immer noch ständig im Einsatz ist) auf einer Kommandozeile (also Bash, SSH und Co) sind Parameter die man dem Programmaufruf mitgeben hat. In den meisten Programmiersprachen werden die Parameter in arg oder argv Array übergeben.

Ich finde es nicht weniger ansprechend als die Verzeichnisschreibweise. Ganz im Gegenteil – ich muss als Benutzer ggf. nicht raten was jetzt Parameter und was noch Verzeichnis ist.

 

Posted in Web |

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.

Umstellung von Webprojekten auf UTF-8

Im letzten Artikel habe ich die Grundlagen zusammengefasst, mit denen ich mich beschäftigen musste um mein aktuelles Projekt voran zu bringen. Das Problem war ursprünglich die nicht einheitliche Handhabung diverser Zeichenketten, was bei Benutzer-Inhalten und Sonderzeichen zum berühmten Mojibake führen konnte (aber eben nicht zwingend muss). Nun denn, gehen wir ans Eingemacht und stellen das Projekt auf UTF-8 um, damit habe ich hoffentlich dann einen wichtigen Schritt in Richtung Zukunftsfähigkeit getan. Außerdem bietet sich UTF-8 wegen seiner Kompatibilität zu ASCII an – Programmcode wird dadurch nicht angetastet und PHP ist dankenswerter Weise sehr einfach gestrickt war Text-Ausgaben mittels “echo” oder ähnlichen Befehlen betrifft: Was hintendran als Argument kommt wird genauso wiedergegeben wie es reingekommen ist – Byte für Byte. Damit hat man wenig Probleme, auch wenn eine Seite eben mal doch Sonderzeichen enthält (wie es im Deutschen leider häufiger der Fall ist). Dieser Post kann als grundlegendes Howto verwendet werden, es gilt jedoch immer: “Keine Garantie” und “your milage may vary”

Erster Schritt – Anpassen der Entwicklungsumgebung

Damit man in Zukunft mit der Entwicklungsumgebung weiterhin einfach arbeiten kann, ohne sich jedesmal Gedanken über irgendwelche Sonderzeichen machen zu müssen – wenn man mal eben drei Zeilen Kommentar schreibt, sollte man sich auf dessen Inhalt konzentrieren können. Je nachdem was man verwendet gibt es da verschiedene Stellen die eingestellt werden müssen. Ich verwende Eclipse, dort kann man das Encoding einmal generell (unter Window -> Preferences -> General -> Workspace:  Text-File-Encoding) einstellen.  Zudem sollte man für die verschiedenen Einträge unter “Content Types” sicherstellen, dass per default dort auch UTF-8 genommen wird. Unter Linux habe ich festgestellt, dass es meistens schon vorab eingestellt ist, denn die meisten aktuellen Linux-Distris sind vom System aus schon löblicherweise auf UTF-8 als Standard umgestellt. Ein Blick in die Äste Web -> CSS Files und Web -> HTML-Files schadet auch nicht. Im Ast XML sollte es per Default auch schon passen, aber Nachgucken schadet auch hier nicht.

Nun muss man noch die Projekt-Einstellungen anpassen (wenn man bestehende Projekte hat) – wenn man den Fehler gemacht hatte und ein neues Projekt angefangen hat ohne sich vorher über die Verwendung von Zeichensätzen Gedanken zu machen (sowas sollte auf jeder Checkliste für Projekte stehen, aber wer achtet da schon drauf wenn es mal eben um eine kleines Projekt geht …. das dann später wächst …). Wenn man die Umstellung gemacht hat, kann man einmal Buchstabensuppe sehen, indem man eine Datei öffnet die Sonderzeichen enthält … die sehen nun etwas komisch aus. Alle händisch ersetzen ist nur was für Hartgesottene oder sehr sehr kleine Projekte.

Zweiter Schritt – Datein umwandeln / umcodieren

Es gibt ein praktisches Tool zum Umwandeln <code>iconv</code> das Werkzeug gibt es auch für Windows, aber so richtig mächtig und scharf wird das erst in Kombination mit ein wenig Scripting (bei mir in Bash)


#!/bin/bash

for file in $(find . -type f -name ‘*.php’)
do
iconv -f iso88591 -t utf8 -o $file.utf8 $file
if [ $? -eq 0 ]
then
#echo “$file erfolgreich”
mv $file.utf8 $file
else
echo $file geht nicht $?
fi
done

Was machen wir und warum mache ich da so? Ersteinmal suche ich alle betroffenen Dateien heraus, da ich mit PHP arbeite sind das alle Dateien die auf <code>.php</code> enden. Jede dieser Dateien lasse ich durch Iconv laufen und als neue Datei abspeichern. Man sollte nicht auf die Idee kommen <code>iconv -f iso88591 -t utf8 -o $file $file</code> zu verwenden. Das klappt nur bei kleinen Dateien. Wenn das nicht der Fall ist, hat man evtl. unvollständige Dateien und wundert sich was passiert ist, denn <code>iconv</code> gibt in diesem Fall keine Fehlerme. Mit dem Speichern in eine separate Datei bleibt das Original erst einmal unberührt und man kann sich ggf. auf die Fehlersuche machen, denn: Wenn beim Konvertieren selbst etwas schiefgeht gibt es einen exit-Code > 0. Wo es geklappt hat, überschreibe ich das Original. Ansonsten ein kleines <code>echo</code> das mir die fehlerhaften Dateien angibt damit man dort nachsuchen kann, meistens hat man schon irgendwo mal etwas als UTF8 gespeichert oder die Zeichensätze verbeult, das findet sich aber meist recht schnell. Bei mir waren es von 500 Dateien zwei Stück die etwas Handarbeit erforderten. Das gleiche Schema kann man auf alle reinen Textdateien anwenden, wenn man es braucht, z.B. reine HTML-Dateien (Endung htm oder html) oder auch JavaScript-Code. Wichtig ist noch dass man seinem Webserver ggf. beibringt, per default auch in UTF-8 auszuliefern. Dazu sucht man in den Konfigurationsdateien die folgende Zeile:

AddDefaultCharset UTF-8

Nach einem Neustart des Daemons wird das dann als default verwendet. Warum aber nur im default-Fall? Klar es gibt noch ne Möglichkeit das einzustellen und zwar in den ganzen HTML-Headern. Dort gibt es das META-Tag mit dem man Vorgaben machen kann auch für den Fall, dass man auf dem Server keinen Zugriff auf die Einstellungen des Apache oder wie auch immer sonst gearteten Webserver-Prozess hat.

&lt;meta equiv="content-type" content="text/html; charset=utf8"&gt;

Damit sollte jeder aktuelle und die allermeisten der älteren Browser in der Lage sein zu erkennen: Jetzt kommt was in UTF8.

Wenn man nur eine statische Website hat, ist man jetzt schon fertig – nur wer hat das schon heute noch?

Datenbankumstellung (MariaDB/MySQL)

Die Website an sich sollte nach der Umstellung weiterhin funktionieren, denn der eingebettete PHP-Code ist besteht nur aus Zeichen des ASCII-Repertoires und ist somit nicht verändert. Über das was zwischen Klammern steht oder einfach “unverdaut” ausgegeben wird, macht PHP sich praktischerweise keine Gedanken sondern gibt es einfach aus, egal wie viel Bytes das sind und ob das nun ein besonderes Zeichen ist oder nicht.

Unlustig sind die Dinge die jetzt noch dynamisch aus der Datenbank nachgeladen werden. Wenn man es bisher (wie häufig bei Standardinstallationen) versäumt hatte auf UTF8 umzustellen, dann muss das jetzt auch noch erfolgen. Ich halte nichts davon die Daten bei jeder Ausgabe in UTF8-Strings zu konvertieren – das ist viel zu viel Aufwand wenn das Projekt eine gewisse Größe erreicht hat. Im übrigen haben viele Leute noch per default den Zeichensatz auf “latin1-swedish” stehen, ein Hinweis woher MySQL denn ursprünglich mal kam. Im 0815-Tagesgeschäft fällt das aber in Europa meist niemandem auf.

Praktischerweise gibt es die Funktion
ALTER TABLE xyz CONVERT TO CHARACTER SET charset_name [COLLATE collation_name]

Damit kann man jeweils eine ganze Tabelle umstellen und eine Collation vorgeben, wir erinnern uns: Collations regeln die Sortierreihenfolge je nach dem wie es gehandhabt werden soll.

Das klingt nicht schlecht und klappt im Allgemeinen auch ganz hervorragend, wenn da nicht einige Fallstricke wären.

Der erste ist ja noch gut gemeint seitens der Engine-Designer: Möglicherweise zu kleine Felder werden in das nächstgrößere Format überführt, wenn der Ausgangszeichensatz einer mit nur einem Byte pro Character ist, der Zielzeichensatz aber mehrere hat oder zumindest haben kann. So werden Textfelder mit bis zu 256 Zeichen zu Mediumtext umgewandelt. Das ist von der Auswirkung her eigentlich zu verschmerzen, aber in meinem Fall habe ich das hinteher wieder manuell korrigiert. richtig problematisch wird das wenn man über die entsprechenden Spalten Indizes angelegt hat, denn Textspalten lassen sich nicht indizieren.

Womit wir beim nächsten Fallstrick wären: An einigen Stellen funktioniert das mit der Umstellung nicht wie geplant, Ursache sind die Foreign-Keys. Ich habe an einigen Stellen “sprechende” Schlüssel anstelle von numerischen verwendet. Gerade dann wenn man in den referenzierten Tabellen wirklich nur Sateliten-Informationen stehen, wie etwa ein Icon und eine ausführliche textuelle Beschreibung zu einem bestimmten Zustand. Einige werden jetzt die Hände über dem Kopf zusammen schlagen, aber ich habe bisher keine negativen Auswirkungen auf die Performance feststellen können, eher positive Effekte bei der Fehlersuche. Wenn man schon anhand des Eintrags in der Tabelle selbst weiß was Sache ist, kann man sich einen zusätzlichen Join sparen. Das hat ja auch seine Vorteile.

Was also tun? Im Netz findet man praktische Anweisungen, die gesamte DB einmal per  mysqldump in eine Textdatei zu konvertieren, dort dann mit iconv das Gleiche zu veranstalten wie mit den Code-Files, danach noch die Tabellen-Definitionen automatisiert anpassen und den Dump wieder in die Datenbank kippen. Das funktioniert, ist aber bei größeren Datenmengen unhandlich.

Stattdessen habe ich mir die Information-Schema-Tabellen zu Nutze gemacht: dort stehen die Meta-Informationen zu den Tabellen in Tabellen, die man wie gewohnt mit SQL-Befehlen lesen kann – schreiben leider nicht, aber immerhin etwas. Dort sucht man sich nun die betroffenen Spalten und Tabellen heraus (Varchars und Texte), zudem schaut man auf die Foreign-Keys und merkt sich diese. Dann schmeißt man die Foreign Keys weg, führt die Veränderungen an den Tabellen durch (das geht ja dann). Wenn das durchgelaufen ist, kann man noch die vergrößerten Spalten wieder auf die Original-Größe zurück setzen (also Mediumtext => Text usw). Zu guter Letzt schaltet man die Foreign-Keys die man sich gemerkt hat wieder scharf. Fertig ist die Laube. Ganz so einfach ist es im Detail nicht, denn man muss stets vollständige Spaltendefinitionen verwenden, einfach nur den Typ ändern geht so leider nicht, auch wenn das eine praktische Sache wäre.

Jetzt hat man das gesamte Projekt umgestellt – zumindest was die Datenbasis und was die HTML-Seiten und den eingebetteten JS-Code betrifft. Das ist in aller Regel alles was man braucht.

Was noch an Problemen auftreten kann, beschreibe ich in einem weiteren Artikel. Stichwort sind hier externe Libraries und Frameworks.

Mojibake – Buchstabensuppe

Na da hatte ich mir doch eine lange währende Aufgabe ausgesucht, die ich in der aktuellen Pflegephase meines Softwareprojekts endlich einmal angehen wollte. Aktuell läuft das Produkt soweit zufriedenstellend, nur an einigen kleiner Ecken knackt und knirscht es immer mal wieder. Unter anderem das leidige Thema “Sonderzeichen und deren Darstellung” – da nicht absolut zwingend für die Funktionsfähigkeit habe ich das längere Zeit nicht großartig beachtet.

Nun gut, was ist das eigentliche Problem und woher kommt es? Die Ursachen liegen in der Historie des PCs ver- oder besser begraben: Da Speicher und Datenverarbeitung teuer war, hat man so sparsam wie möglich gearbeitet. Man stellte dabei fest: Inklusive aller wichtigen Zeichen der englischen Sprache und allem was man als Steuerzeichen (Zeilenumbruch, Tabulator, etc.) benötigt, bekommt man in 7 Bit unter. Das ist schon mal etwas krumm, denn gängig ist bei allem was Rechner betrifft doch eigentlich etwas eine Potenz von 2. Die ersten Rechner die ich verwendet habe (mit einem Intel 8088 als Unterbau) konnten nur 8 Bit parallel verarbeiten – immer mehr als die 7. Schon wenig später gab es 16Bit als Standard und mit der großen Verbreitung des PCs in die Büros war der Standard 32 Bit erreicht. Heute findet der sich bereits größtenteils in Ablösung durch 64Bit-Systeme. Was war der Sinn des 8. Bits? Man konnte es je nach Gusto verwenden: Entweder bohrte man damit den Zeichenvorrat auf, oder man konnte es zur Fehlererkennung und Korrektur einsetzen. Insgesamt muss man sagen: Alles mit Hand und Fuß.

Nur leider waren Rechner damals noch nicht gängigerweise vernetzt und die Standard-Definitoren etwas kurzsichtig – erst einmal wurde nur an die eigene Heimat gedacht, dass es möglicherweise andere Sprachen gibt, die nicht Englisch sind und einige Zeichen mehr mitbringen – wer konnte das schon ahnen? So kam was kommen musste: Einschränkungen bei diversen Dingen wie dem Betriebssystem: Umlaute oder Sonderzeichen in Dateinamen waren einfach nicht vorgesehen oder führten zu unvorhergesehenen Ergebnissen. Für das Verfassen und Verarbeiten von Texten war das natürlich keine Option, wer in Deutschland einen Text schreiben wollte, der sollte das auch tun können. Daher erweiterte man den Zeichensatz auf das 8 Bit und schon hatte man jede Menge Möglichkeiten auch Sonderzeichen abzuspeichern. Wichtig war nur, das jeder die gleiche Zuordnung traf und nicht einer ein ü für ein ö verwendete was die binäre Darstellung betraf. Soweit so gut oder auch schlecht.

Nun gehen leider bei verschiedenen Betrachtungen immer wieder einige Dinge durcheinander, auch ich habe hier am Anfag so meine liebe Mühe gehabt alles richtig zu verorten, daher hier einmal die Kurzfassung, zu allem was notwendig ist bzw. was sich auf die Darstellung und Handhabung von Texten am Rechner auswirken kann.

Binärer-Code

Eine Abfolge von 0en und 1en die irgendetwas darstellen – was ist für den Rechner erstmal unerheblich, dafür bedarf es einer Zuordnungs oder Code-Tabelle oder eines Algorithmus, der den Binärcode in etwas umsetzt, das dem Benutzer eher vertraut ist (leider sind wir Menschen nicht sonderlich gut binär veranlagt).

Code-Tabelle/Zuordnung/Algorithmus

Je nachdem was für eine Datentyp abgebildet werden soll, gibt es verschiedene Methoden dies zu tun. Am einfachsten sind ganze, positive Zahlen. Diese lassen sich aus der Abfolge von 0en und 1en entsprechend “errechnen” – man muss nur noch wissen wo man anfangen muss (little endian vs. big endian). Für positive und negative Ganzzahlen hat sich das Zweierkomplement bewährt, für Fixkomma und Gleitkomma gibt es entsprechende Standards. Alles ein Thema für sich, denn wir wollen ja Texte bzw. Strings beleuchten. Hier hat sich eine Übersetzungstabelle als Mittel der Wahl erwiesen – sie ordnet jeder möglichen Abfolge von 0en und 1en eine menschenlesbare Bedeutung zu. So wird z.B aus der binären Folge 0110 1101  = 0x6D = O155 = “m”. Die Zuordnung an und für sich ist reine Willkür – man hat sich mal auf gewisse Standards geeinigt bzw. diese festgelegt, damit eben auf allen Rechner ein “m” ein “m” ist und nicht aus der “e-mail” durch Willkür eine “e-nail” wird. Der Urvater aller Standards dazu heißt ASCII (America Standard Code for Information Interchange) – wie der Name schon sagt ein nationales Standardformat. Auch bekannt ist diese Tabelle als Character-Set.

Windows-Codepages und Nornumg

Für verschiedene Regionen wurden verschiedene Verwendungen des verbliebenen 8. Bits (siehe oben) standardisiert – leider teilweise recht unterschiedlich, zum Teil nur an wenigen Stellen. Ziel war es, die in der jeweiligen Region verwendeten Sprachen möglichst gut abzubilden. Leider gab es da erst sehr spät eine Einigung auf einheitliche Standards, so dass zeitweise unterschiedliche Hersteller unterschiedliche Zuordnungen trafen. Ganz genauso wie wenn man Dateien ohne Angabe des verwendeten Zeichensatzes weitergab konnte es also passieren, dass der Zielrechner die Bit-Folge unterschiedlich interpretierte, weil bestimmte Bitfolgen eben etwas unterschiedliches bedeuteten. Dankenswerterweise hat man dabei die sichtbaren Zeichen (leider nicht so bei den Steuerzeichen) aus dem Urvater ASCII gleich belassen (das hatte sich ja bewährt, und für den internationalen Textaustausch reichte dieser kleinste gemeinsame Nenner auch aus). Gängig sind in der westlichen Welt die Standards der ISO 8859 in den Varianten 1-16, unter Windows die Codepage 1250 und unter DOS die Codepage 850.

Unicode

Mit der steigenden Internationalisierung und der immer stärker zunehmenden Vernetzung von Computern ist das mit den Nachschlagetabellen so eine Sache – man muss immer wieder etwas anderes berücksichtigen: Das Betriebsystem und die Region bzw. die Regionaleinstellungen des Anwenders – nur damit Text auch so ankommt wie er einegeben wurde. An einigen Stellen hat man sich beholfen oder die Probleme durch menschliche Intuition überbrückt – durch die Redundanz in der menschlichen Sprache kann man viele Worte ja auch lesen wenn Buchstaben vertauscht wurden oder nicht lesbar sind. Jeder Mensch der Lesen lernt kann das mehr oder weniger: Wenn aus einen Königreich dann plötzlich ein K$nigreich wird, dann wird der geneigte Leser das immer noch erkennen, auch wenn so ein Text etws mühsamer zu lesen ist. Ohnehin: wenn es international wird, dann ist die Sprache der Wahl in aller Regel doch Englisch und somit problemfrei. Aber wäre es nicht toll, wenn jemand eine Lösung hätte, damit ich auch chinesische Texte richtig angezeigt bekomme? Oder auch mal einen arabischen Text im Original anschauen? Das klingt weit hergeholt, aber diese Anwendungsfälle gibt es häufiger als man denkt. Die ursprüngliche Idee mit 32Bit und somit 4 Bytes pro codiertem Zeichen zu arbeiten (UTF-32) ist zwar einfach zu realisieren, aber ein wenig “over the top” ist das schon: Wer überträgt schon gerne 3 komplett genullte Bytes wenn die Info doch nur in einem steht? Außerdem ist der Standard nicht abwärtskompatibel, er bricht mit den alten Vorgaben von ASCII, was bei älterer Software oder inkompatibler Software Ärger machen kann. Daher gibt es verschiedene Methoden mit variabler Anzahl von Zeichen, sozusagen das Beste aus beiden Welten: UTF-16 oder UTF-8 sind hierbei die bekanntesten Möglichkeiten.

Schriftarten und Glyphen

Wir haben uns nun langsam von der Bits & Bytes-Ebene nach oben gearbeitet hin zum abstrakten Begriff eines “characters”. Es gibt aber noch eine Ebene obendran – die Schriftarten oder auch Glyphen. Das ist im Prinzip eine weitere Tabelle die festlegt wie ein abstraktes Zeichen auf dem Ausgabemedium dargestellt werden soll. Jeder der sich einmal durch den Schriftenkatalog einer gängigen Office-Sammlung gewühlt hat, weiß das ein A ein A bleibt, auch wenn es etwas unterschiedlich geformt ist. Das beste Beispiel ist noch immer die eigene Handschrift im Vergleich zu einem beliebigen, gedruckten Werk. So lange eine gewisse Basis-Form erhalten bleibt, erkennt jeder Leser darin auch weiterhin ein A. Es gibt natürlich auch wieder Spezielle Schriftarten die als Darstellungvon Buchstaben Symbole haben, die nichts mit dem eigentlichen Zeichen zu tun haben – das bekannteste dürfte die Schriftarte Wingding oder auch Webdings sein. Besonders unpraktisch fällt mir diese immer wieder in e-mails aus Outlook auf, wenn man diese als Text ließt. Dort wird der Smily 🙂 automatisch in ein J umgewandelt und mit der Schriftart Wingdings formatiert – sieht zwar im ersten Moment richtig aus, aber mancher hat sich schon gewundert wo das “J” denn nun herkommt. Ich merke es mir immer so: Ein Glyph macht aus einer Bit-Folge mittels einer Tabelle eine ganz bestimmte Grafik. Das ist zwar etwas vereinfacht, denn Schriftarten machen manchmal noch mehr, aber um den Überblick zu behalten reicht es allemal.

Collations

Was nochwas? Haben wir nicht alles endlich abgehandelt? Leider noch nicht ganz: Es gibt nämlich noch etwas was man mit Zeichenketten gerne macht: Aufreihen und Sortieren. Kling trivial, und es gibt doch Sortieralgorithmen wie Sand am Meer. Könnte man meinen, leider ist dem nicht ganz so (und das ist mit ein Grund weshalb man Sortieralgorithmen am besten an Zahlen erklärt …), denn auch wenn es im ersten Moment verlockend sein mag zu sagen: Man sortiert einfach nach der Größe der entsprechenden Ganzzahl der Bitfolge, das bringt leider nicht das gewünschte Ergebnis, denn schon bei ASCII haben wir ja große und kleine Buchstaben, ordnet man nun nach der errechneten Wertigkeit, so würde das folgende Sortierreihenfolge ergeben A,B,C…..,X,YZ,a,b,c – für die alphabetische Auflistung eines Materialkatalogs nicht das gewünschte Ergebnis – auch im Telefonbuch sucht man die Leute mit “von” im Vornamen ja nicht unter “v” sondern unter “V” und dort irgendwo nach “Voldemort”…. und nun wirds ganz interessant: Wo ordne ich denn die Sonderzeichen wie Ä,Ö,Ü ein … sind das separate Zeichen am Ende? Oder soll ich sie behandeln wie “Ae”,”Oe” und “Ue”? Das ganze nun noch auf die internationale Ebene gehoben und es wird ganz spannend: Je nach Land gibt es gleiche Zeichen, aber die werden unterschiedlich einsortiert, aber der Rechner soll es dennoch richtig machen. Das kann nur bedingt funktionieren, alles weitere regelt man über die Collation, die kann man zur Not auch bei der Sortierung mit angeben, und dem Sortierprogramm somit die Regeln vorgeben.

So jetzt habe ich einmal die Grundlagen zusammengefasst, die Auswirkungen und Mittel die man braucht um das alles umzusetzen behandle ich in einem separaten Artikel.

 

 

So bitte nicht – bad / worse practices – ein Kessel Buntes

Das die Welt nicht grau in grau ist (oder zumindest aktuell sehr zügig endlich der Frühling mit seiner Blüten und Farbenbracht Einzug hält), erfreut das Gemüt. Anders geartet ist da der Datenbank-Entwickler – wie jeder Informatiker ist er “kühl und dunkel” zu lagern und zu halten, damit er optimal arbeitet.  Somit einher geht eine verstärkte Reaktion auf Restlicht (auch das des Monitors), allerdings messerscharf in schwarz/weiß oder zumindest in Graustufen. Spaß beiseite: “Ein Kessel Buntes” ist eine finde ich recht treffende Umschreibung für das was landläufig oftmals als “Datenbank” verkauft und verstanden wird. Leider wieder eine Praxis die auf Dauer nicht tragfähig ist.

Was ist der “Kessel Buntes” und warum ist er keine gute Idee? – Generell bezeichne ich mit diesem Ausdruck nicht richtig getrennte und beschriebene Entitäten. Teilweise trifft das auch nicht richtig spezialisierte Entitäten. Immer wieder trifft man auf diese Art des Datenbank-Designs: Oftmals wird sie aus der Not heraus geboren, oder aus dem Missverständnis, dass eine Datenbank doch nur eine etwas bessere Excel-Tabelle sei. Eine Excel-Tabelle per se ist ja nicht einmal schlecht – sie bringt immerhin etwas Ordnung ins Chaos und ist für viele tagtägliche Anforderungen das flexible Werkzeug, wenn man mal eben eine Analyse machen muss, oder einfach nur eine “Kleinigkeit” visualisieren soll. Eine Tabellenkalkulation ist hierzu sehr flexibel und bietet alle Möglichkeiten die man sich wünschen kann – von Layout bis hin zur Berechnung – nichts ist beschränkt. Genau diese Flexibilität ist es jedoch die bei größeren Projekten schnell vom Vorteil zum Hindernis werden kann. Mit der Flexibilität einer Tabellenkalkulation kann man sich oftmals die doch eher lästige tiefgehende Analyse eines Sachverhaltes für eine Datenbank sparen. Problematisch wird es, wenn Tabellenkalkulationen dann zum Allheilmittel erkoren werden. Ehe man es sich versieht werden da komplexe Matrizen aufgestellt, die dem menschlichen Benutzer die Information schön handlich aufbereiten. Nur die Maschinenlesbarkeit ist dann nicht mehr unbedingt gegeben, und selbst einfach Analysen im Datenbestand arten zur Sisyphus-Arbeit aus, vor allem wenn die Flexibilität voll genutzt wird und jede Matrix ein klein wenig anders aussieht.

Besonders ärgerlich aus Datensicht ist hierbei die Verquickung von Äpfeln und Birnen zu Obstsalat. Ich selbst durfte hierzu ein sehr schönes Beispiel einer gewachsenen Datenbank erleben: In einem Unternehmen gibt es verschiedene Aufträge, mit unterschiedlichen Qualitäten und Eigenschaften. So gibt es Aufträge die intern erzeugt werden und nur innerhalb von Abteilungen verrechnet werden, wenn überhaupt, zudem gibt es Aufträge, welche mit externen Elementen (auch als Kunden bezeichnet) abgewickelt werden. Nun war der Entwickler etwas faul oder es hat sich erst im Laufe der Zeit ergeben: Für die beiden Typen sind unterschiedliche zusätzliche Informationen notwendig. Initialer Weg um Zeit und Arbeit zu sparen: “Es ist ja nicht viel was da dazu kommt, wir fügen einfach Spalten an”. Recht zügig kann man in einer Datenbank der Einsteigerklasse (die sich tatsächlich auch noch als Datenbank bezeichnet) solche Änderungen realisieren. Die Rede ist hier von einem recht bekannten Produkt aus dem Hause “Winzig-Weich”. Problematisch ist bei diesem Produkt schon die Tatsache, dass Datenhaltung und Repräsentation scheinbar nahtlos ineinander übergehen. Für Anfänger ohne große Vorkenntnisse senkt das die Hemmschwelle doch ganz erheblich. Auch das ist ja für sich genommen eine löbliche Sache, nur auch dieses Produkt hat einen Einsatzbereich (der ist zugegebener Maßen recht breit) – wenn man über diesen hinaus wächst (und viele Projekte haben die Tendenz dazu), merkt man irgendwann recht heftig, wo es zwickt und kneift.

Nun haben wir eine aufgebohrte Datenbank-Tabelle – etwas, dass man mit ein klein wenig Aufwand auch in anderen Datenbanksystemen machen kann. Aus Erfahrung weiß ich nur zu gut, dass es immer wieder Fälle gibt, in denen man sich aus gutem Grund dafür entscheidet eine Spalte “Overhead” zu spendieren, die nicht immer gefüllt wird, oder sogar nur in wenigen Fällen einen von NULL unterschiedlichen Wert hat. Man hat zwar dabei ggf. ein leichtes Bauchgrimmen, aber es gibt durchaus Szenarien in denen es weniger auf die Performance im Speicherverbrauch oder die absolute Performance ankommt, es aber mit den zusätzlichen Spalten recht schnell greifbare und brauchbare Ergebnisse gibt.

Das Ganze kann man jetzt noch weiter auf die Spitze treiben, in verschiedenen, nicht gegenseitig-exklusiven Geschmacksrichtungen (oder vielleicht doch besser Geschmacksverirrungen):

Man nehme einen weiteren Auftragstyp dazu, weil das aktuelle Projekt es erfordert: Da man in der Tabellen-Ansicht ja jetzt Felder hat die unbelegt sind und man die spezifischen Daten des neuen Typs ja auch noch speichern muss, fängt man kurzerhand an zu Tricksen und zu sparen: Bestehende Felder werden je nach Auftragstyp umgewidmet. Je nachdem was für einen Auftrag man gerade hat, bekommen die Felder jetzt eine Abhängigkeit, eine Semantik. Das macht die Arbeit bei den Masken recht einfach, auch eine tabellarische Übersicht ist kein Problem  – man muss nur wissen wie die einzelnen Felder jetzt zu interpretieren sind. Besonders spaßig ist dann natürlich die Verwendung falscher Datentypen, weil es halt doch nicht mehr so recht gepasst hat: Man kann auch ein varchar-Feld dazu verwenden um Datumsangaben oder Integerwerte zu speichern. Typecast gemäß Semantik und die Sache ist geritzt…

Die Performance und Wartbarkeit dieses Konstruktes kann sich der geneigte Leser dann mal selbst überlegen – vom Wechsel des Datenbank-Unterbaus hin zu einem professionellen Server mit ggf. sehr scharfer referenzieller Integrität wollen wir lieber einmal gar nicht träumen, diese wird dann oftmals auch einfach “geopfert”.

Zweite besonders zu empfehlende Möglichkeit die Performance noch weiter zu minimieren und dem ganzen ein unbeschreibliches “Geschmäckle” zu verpassen: Die Unterscheidung der Typen ist nicht eindeutig oder klar umrissen, sondern kann womöglich sich zur Laufzeit noch ändern. Viel Spaß schon einmal beim Umsortieren und uminterpretieren der oben missbrauchten/umgewidmenten Felder. Auch Typecast-Mortale genannt (nicht unmöglich und bei heutiger Rechenpower fällt es nicht mal so sehr auf, zumindest für kleinere Datenmengen). Damit die Änderung leicht fällt bzw. weil man sich diesmal um die Felder und die notwendigen Anpassungen des semantischen Codes drücken will (wir erinnern uns: Das ist so sonderlich gut wartbar), macht man es wie in der Realität so häufig auch: Man verwendet “sprechende Schlüssel bzw. sprechende Identifikatoren”. Man kennt diese zur Genüge au vielen Bereichen – so lange der Benutzername beschränkt war auf 8 Zeichen, hat man einfach den ersten Buchstaben des Vornamens plus die 7 ersten des Nachnamens genommen. Wenn es doch mal zu Überscheidungen kommt, gab es eben fortlaufende Endziffern. Nicht schön aber es funktioniert. Ähnliches kann man teilweise bei Dokumenten aus Buchhaltungs-Systemen beobachten: Die ersten Zeichen definieren den Typus des Dokuments, die weiteren sind ggf. noch an das Datum gekoppelt oder gleich einfach fortlaufend. Gängig sind Angaben wie “ANG-xyz” für Angebot Nr. xyz odr der LS-4711 für den Lieferschein mit der Nummer 4711. Weitere Beispiele kann man sich leicht vorstellen.

Die Performance wird allerdings besonders grottig, wenn man diese Typ-Information aus bestimmten Gründen nicht in einer separaten und indizierten Spalte (sehr einfach und dennoch effektiv sind z.B. Enumerations sofern die Datenbank mit dieser “Mini-Foreign-Key-Lösung” umgehen kann) sondern einfach die gesamte Nummer in ein Varchar-Feld packt. Wenn man nun nach unterschiedlichen Typen filtern möchte, muss man doch nur die ersten Zeichen betrachten (vorzugsweise noch unterschiedliche Längen wie etwa: ANG und LS von oben, zusammen mit ANS für Kostenvoranschläge…). Mit dieser Technik bekommt man jede Datenbankengine ins Trudeln. Anti-Performance ist at its best …

Nun gut, genug gemault und Augen verdreht – woher der Ausdruck “Kessel Buntes” kommt dürfte nun jedem klar sein: Man hat verschiedenste Obst (und ggf. auch Gemüse)-Sorten (im Datenbank-Bereich auch Entitäten genannt) in einer Tabelle zusammengeführt und über die Zeitachse das ganze gut durchgerührt und etwas ziehen lassen … Zeit dafür Lösungen zu präsentieren wie man es besser macht.

Oberstes Gebot bei der Modellierung bzw. dem Abbild der Realität: Entitäten trennen, wie Eiweiß und Eigelb. Beides gehört zwar zur Entität “Ei” aber die Eigenschaften unterscheiden sich schon von der Farbe und dem weiteren Verwendungszweck beim Backen 😉 Daher: Wenn sich zeigt, dass eine weitere, vielleicht auf den ersten Blick sehr ähnliche Entität hinzugenommen werden soll, sehr sehr kritisch prüfen ob man diese unbedingt in das bestehende Schema “pressen” muss. Im ersten Moment mag es zwar mehr Arbeit sein separate Entitäten zu verwalten, aber die Erfahrung zeigt: Jedes Datenbankschema hat auf die mittlere bis lange Frist die Tendenz sich weiter auszudifferenzen und detaillierter zu werden. Also besser gleich von Anfang an verschiedene Tabellen verwenden (ich werde auch noch einen ausführlichen Beitrag zum Thema “one true lookup table – OTLT”  verfassen) – diese im Zweifel per UNION erst einmal wieder zusammen zu führen (was nicht immer vermeidbar ist, oder zumindest für einen Teil der Daten sinnvoll sein kann) ist der bessere Weg.

Im letzten Absatz ist es schon angeklungen, ein Problem das man häufiger hat: “Aber die Dinger aus der Realität sind doch zu 80% ident …” – in der objektorientierten Programmierung ist das ein klarer Fall für die Verwendung von Vererbung ggf. in Kombination mit abstrakten Klassen. Die übergeordnete Klasse stellt die gemeinsame Basis samt Funktionen bereit, und die spezialisierten Klassen kümmern sich um die 20 verblieben Prozent der Details. In einer relationalen Datenbank klappt dieser Ansatz nicht direkt. Hier muss man auf die Kombination von Geschäftslogik und einzelnen Tabellen zurück greifen: In der Geschäftslogik verwendet man den objektorientierten Ansatz wie gerade beschrieben, in der Datenbank führt man Surrogate ein, die es erlauben vom Kleinen auf das Größere zu schließen – dabei muss man ggf. aufpassen, dass keine Doppeldeutigkeiten entstehen (teilweise sind diese jedoch auch wünschenswert, das muss man situativ entscheiden) gibt. Auch bekannt ist das Konzept unter dem Namen “Vererbung für Arme”. So bekommt man zwar auch ein wenig Overhead, aber die Struktur bleibt klar erkennbar und man braucht nicht erst noch eine Tabelle die einem sagt, wie jetzt welcher Wert zu interpretieren ist.

Was auf keinen Fall eine Option sein darf, ist die Integration von Typinformationen in ein anderes Feld – diese Information muss man wo immer möglich in separate Felder auslagern, die man im besten Fall noch mit einer Referenz (Fremdschlüssel) auf eine andere Tabelle mit entsprechenden Meta-Informationen absichert. Sonst passiert es leicht, dass der Benutzer (aus Schusseligkeit oder gar mit Intention) einen neuen Typ einführt der gar nicht definiert ist (z.B. ein Tippfehler der Form “AND” anstelle “ANS”) – welche Semantik dann greift bzw. welch kuriose Fehlinterpretationen und Fehlermeldungen dann auftreten können liegt im Bereich der Spekulation und der Admin-Belustigung. Über die notwendigen Operationen der String-Extraktion denkt man in diesem Fall auch besser nicht nach, geschweige denn daran wie häufig diese Operation angewandt werden muss…

Fazit: Es mag verlockend sein, eine Datenbank möglichst flexibel und “platzsparend” zu gestalten, wie man es auch in einer Tabellenkalkulation machen würde. Aber die gewonnene Sicherheit ist absolut trügerisch und der Performance und Wartbarkeit tut man sich mit einem Kessel Buntes mit absoluter Sicherheit keinen Gefallen. Besser gleich überlegen ob das wirklich notwendig und sinnvoll ist, so klein das Problem aktuell auch sein mag – die Wahrscheinlichkeit, dass man irgendwann einen großen Scherbenhaufen hat ist erheblich vergräßert und dann ist nichts gewonnen, aber viel verloren.

Wenn der Postmann zweimal klingelt – MBox, Thunderbird, Maildir und IMAP

Na da hatte ich mir doch mal wieder eine nette kleine Bastelaufgabe gestellt, als ich einem Kumpel versprochen hatte ihm bei seinen e-mail-Problemen ein wenig zu unterstützen. Ausgangssituation war ein Rechner-Zuwachs bzw. die Nutzung von Smartphone, Laptop und Festrechner. Den e-mail-Account hatten wir schon vor ettlichen Jahren eingerichtet – das war schon so lange her, dass ich mich kaum daran erinnern konnte. Ein deutlicher Hinweis auf das Alter war die Verwendung des Post-Office-Protokolls Version 3 (kurz POP3) – damals noch die Standard-Technik zum Abruf von Mails. Bisher war mein Kumpel mit dem System recht gut gefahren und auch Thunderbird als Mail-Client kommt ja problemlos mit POP3 zurecht.

Was sind die Rahmenbedingungen für POP3 und warum wird es heute nicht mehr verwendet oder besser gesagt nur noch da wo es nicht anders geht? – Sicherlich nicht weil es einfach nicht mehr “in” ist. Vielmehr war zu den frühen Zeiten des Internets (die ich noch erleben durfte) eine unbeschränkte Verbindung ins Netz nicht gegeben (ja es gab eine Zeit vor den Flatrates und DSL – heute kaum mehr vorstellbar) – damals wählte man sich per Modem ins Netz ein – bezahlt wurde nach Minuten-Tarifen. Daher war es wichtig die Leitung möglichst effektiv auszulasten, zumal man in der Regel mit nur einem Anschluss pro Haushalt in der Zeit nicht telefonieren oder Faxe schicken konnte (ISDN war damals das Zauberwort – zwei Kanäle, drei Rufnummern gab es standardmäßig – ich gehörte leider nicht zu den glücklichen Nutzniesern). Nach dem Aufbau der Verbindung holte man seine Mails ab, surfte diverse Seiten an und trennte die Verbindung wieder wenn man sie nicht benötigte. POP3 unterstütze genau diese Funktionalität: Alle Mails aus der Mailbox nacheinander herunterladen auf den eigenen Rechner und dann war erst mal Ruhe. E-mail war ja schon schnell, aber dennoch musste man damals nicht sofort bei jeder e-mail informiert werden (was ich auch heute nur sehr bedingt brauche). Man beantwortete die Mails und sammelte sie, danach baute man die Verbindung für den Versand wieder kurz auf und speißte die Mails ins Internet zur Verteilung ein.

Die Vorteile liegen klar auf der Hand: Die damals wertvolle Bandbreite und Übertragungszeit wurde so möglichst effektiv ausgenutzt. Klar sind aber auch die Nachteile: Verteilte Nutzung von e-mails auf mehreren Rechner war nicht vorgesehen, auch wenn man die Mails in der Mailbox belassen konnte (die dann irgendwann überlief und man keine Mails mehr empfangen konnte – damals waren 5-10MB Mailspeicher schon sehr viel, aber es wurde ja auch nicht jeder Sch… per e-mail weiter verteilt). Schon zu meinen ersten Gehversuchen existierte das konkurierende Protocol Internet Message Access Protocol (IMAP) – das löste das Problem mit der Synchronisation durch eine zentrale Speicher-Instanz. Leider zu dem Preis, dass man in der Regel eine permamente Internet-Verbindung benötigte oder zumindest eine zum e-mail-Server. Gegenüber POP3 war IMAP ein deutlicher Fortschritt – Serverseitige Filterung war ebenso möglich wie verschachtelte Unterordner in der Mailbox (alles was man heute als nahezu selbstverständlich wahrnimmt) – ursprünglich entwickelt für den Mailverkehr innerhalb von Firmen (die schon damals oftmals eine Standleitung hatten). Indirekt populär wurde das Protokoll durch die Entwicklung von Webmail-Services – auch meine erste e-mail-Adresse nutze ich auf diese Art und Weise, da sie unabhängig von weiterer Software war, die auf Schulrechnern ohnehin nicht installiert werden durfte. Als Schnittstelle wurde das Common Gateway Interface genutzt – in aller Regel werkelte dahinter eine Reihe Perl-Skripte.

In den ersten echten Genuss von IMAP kam ich durch das Aufsetzen eines internen Mailservers für Heimzwecke – der holte die Mails per POP3 in festen Intervallen ab und stellte sie dann intern wieder per IMAP zur Verfügung – ich wusste schon zu der Zeit die Vorzüge zu genießen. Selbst ist der Admin 🙂

Der langen Rede kurzer Sinn – Ziel war es nun, das eingerichtete e-mail-Konto von POP3 auf IMAP über zu siedeln. Ich selbst habe diesen Schritt schon häufiger durchgeführt – auch mit Thunderbird. In der Regel ist das kein Problem: Ein zusätzliches Konto per IMAP einrichten, den POP3-Abruf stilllegen, damit er nicht die gerade hochgeschobenen Mails gleich wieder abholt und löscht (den Fehler merkt man recht flott …) und dann kann man mit einer schönen Operation “Drag&Drop” die e-mails aus den lokalen Ordnern auf den Server verschieben. Eine Sache von wenigen Minuten in der Regel.

Doch grau ist bekanntlich alle Theorie – irgendwie wollte das nicht so recht funkionieren – der Transfer zog sich ewig in die Länge und brach dann irgendwann einfach ab. Da es bereits etwas später am Abend war, haben wir es erstmal vertagt.

Ich habe mich dann mal etwas genauer schlau gemacht was da ursächlich sein könnte bzw. wie man das optimieren kann. Kurze Recherhe mit “Prof Dr. Dr.  Google” liefert einen Ansatzpunkt: Thunderbird verwendet für lokal gespeicherte e-mails ein standardisiertes und weithin bekanntes Format zum Speichern von Mails: das gute alte MBox-Format – ein recht einfaches Text-Format in dem alle e-mails in der Reihenfolge des Eingangs hintereinander weg geschrieben werden. Einfache Techniken sind ja in der Regel begrüßenswert, aber in diesem Fall ist die Extraktion nicht wirklich einfach (Regular-Expressions sind sicherlich hiflreich, aber auch die diversen Fragen zum Escaping der From-Lines – näheres siehe im Link). Damit es etwas schneller geht fertigt Thunderbird eine Index-Datei an. Unterordner erhalten bei Thunderbird eigene Dateien.

Eingangs hatte ich anklingen lassen, dass man früher nicht gerade große e-mail-Postfächer hatte – daher war das Mbox-Format auch zielführend. Aber man bedenke etwaige Beschränkungen die jeder PC-Nutzer meiner Generation wohl irgendwann einmal kennen gelernt hat: Beschränkuingen des Dateisystems erlauben nicht, dass eine Datei eine gewisse Größe überschreiten darf – mit FAT32 sind es 4GB, früher unvorstellbar groß – heute noch nicht mal ausreichend für einen Film auf DVD. Nun kann man sich natürlich auch vorstellen wie performant die Arbeit in entsprechend großen Text-Dateien ist, wenn man darin Mails sucht … für große Datenmengen gibt es deutlich besser Aufbewahrungsmethoden, zumal man in der Regel die Mails ja nicht auf Endlos-Papier ausdruckt sondern doch eher Stück für Stück liest. Was Thunderbird so flügellahm machte war die simple Tatsache, dass aufgrund des vielen Inhalts fast 3 GB Mails in einer Datei lagen – in Zeiten von dicken Anhängen und den unvorstellbaren Mengen Speicherplatz aktueller Rechner eigentlich nicht mehr verwunderlich.

Was also tun? Wenn Thunderbird ins Straucheln kommt, dann sinnt man auf Abhilfe mit einfachen Mitteln, zumal ja alles standardisiert ist: Protokolle, Format, da sollte sich doch etwas machen lassen. Mittel der Wahl als Administrator ist für solche “Quick’n’Dirty”-Arbeiten die Programmiersprache Perl. Flexibel, teilweise wirklich sehr kurz und unsauber, aber halt einfach das Schweizer Offiziersmesser wenn es um das stapelweise verhackstücken von Dateien geht. Praktischerweise gibt es mit CPAN ja auch für fast jeden Anwendungszweck passende Module. Recht flott habe ich ein Skript zusammengezimmert, das mir die einzelnen Dateien durchgeht und den Upload per IMAP übernehmen soll. Doch, wa soll das schon wieder – auch dieser Weg scheitert – die Perl Module laden nach und nach die gesamte Datei in der Arbeitsspeicher, bis das Betriebssystem meint “jetzt ist aber gut, und den Prozess abwürgt”.

Nun gut, als Admin fühlt man sich nun doch heraus gefordert – wenns so nicht geht, dann gibts ja immer noch Mittel und Wege. Ich entscheide mich dem Tool das Leben etwas leichter zu machen und die Mail-Datei schon einmal etwas vorzuverdauen. Will heißen wir spalten die große Datei in kleinere Happen auf. Mittel der Wahl für mich in diesem Fall ist das Maildir-Format, das sich als Alternative zum Mbox-Format etabliert hat und das mittlerweile viele Mailserver und auch Clients einsetzen (interessanterweise gibt es auch Ansätze ob Thunderbird das lokal verwenden sollte…) Im Maildirformat ethält jede Mail eine eigene Datei – etwas weniger effizient was den Speicherplatz betrifft, aber der ist ja heute reichlich verfügbar und e-mails sind heute meist derart umfänglich, dass dieser kleine Overhead auch nicht mehr ins Gewicht fällt. Generell ist der Zugriff auf eine einzelne Nachricht nun wesentlich direkter möglich.

Auf zu Versuch Nummer 2 als, diesemal dann mit einem Perl-Modul, das e-mails in Maildir-Verzeichnissen lesen kann. Und wieder ein Griff ins Klo … ich zweifle schon an meinen Fähigkeiten, als ich dann doch diverse Reports im Netz finde, dass die Iteration über Mailboxes mit dem Perl-Modul wohl einen Bug hat und den allokierten Speicher nicht mehr rechtzeitig frei gibt. Ein klassisches Memory-Leak also.

Weiter verfolgen muss ich das nun aber nicht mehr, mein Kumpel hat in der Zwischenzeit eine wesentlich bessere Variante gefunden, als Brute-Force alles auf den Server zu transferieren: Einfach vorher das Postfach mal grundlegend entrümpeln und wirklich nur die erhaltenswerten Mails auf den Server verschieben. Das ist natürlich auch eine Lösung, auch wenn sie für mich technisch natürlich nicht an den Charme eines Skriptes heran kommt. Aber was immer hilft soll mir in diesem Fall ja recht sein.

Was haben wir also gelernt: POP3 ist nur noch in Ausnahmefällen eine sinvolle Alternative, vor allem weil heute ja kein echter Mangel an Bandbreite und Dauererreichbarkeit mehr besteht. Selbst für den Offline-Fall bringt heute jeder IMAP-Client notwendige Mechanismen zum Caching der Mails mit. Zweitens: Auch das MBox-Format ist inzwischen mit Vorsicht zu genießen, ich selbst verwende es ja schon seit den Anfängen des eigenen Mailservers nicht mehr (auch weil qmail es damals nicht aktiv unterstützte sondern die Grundlage für Maildir legte). Aber das allerwichtigste ist wohl: “Schmeiß auch mal weg, trenne dich von Dingen die du eh nicht mehr brauchst” – das regelmäßige Bereinigen eines Postfachs macht zwar auch Mühe, aber man kann es ja auch wieder automatisieren: Wer hat jemals e-mails angeschaut, die älter als drei Jahre waren? Wirklich wichtige Informationen speichert man ohnehin als Datei ab oder druckt sie dokumentenecht auf Papier aus. Die ganzen restlichen Alltagsmails kann man nach einer gewissen Zeit gefahrlos löschen. Es lebe der Frühjahrsputz. So und jetzt greife ich dann auch mal zum digitalen Schrubber – das könnte etwas länger dauern – derzeit habe ich rund 5 GB an e-mails herum liegen … da hilft wohl doch eher der Hochdruckreiniger denn ein Schrubber …