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 …

 

 

Typo3 und ImageMagick – was zu beachten ist

Für einen guten Zweck arbeite ich auch als Hosting-Anbieter für die Interessensgemeinschaft Herzogenried. Die Verbindung zum Quatiermanagement entstand schon vor einigen Jahren, damals ging es primär darum überhaupt eine Präsenz im Netz aufzubauen. Der verantwortliche Mitarbeiter war mir schon von daher sympathisch, dass er vieles bis ins Detail verstehen wollte und auch im Netz selbstständig agieren wollte. Er hat sich mit enormen Eifer und einiger Unterstützung damals die Grundlagen von HTML selbst beigebracht.

Zwischenzeitlich ist der Bedarf gewachsen, es wurde umgestellt auf Joomla! als Content-Management-System (CMS) – für mich auch keine große Sache, jedoch habe ich mich nicht in der Tiefe mit dem System auseinander setzen können wie ich es gerne gewollt hätte. Aber die Mannschaft hat es auch so recht gut hinbekommen – einige Kleinigkeiten habe ich noch unterstützend beitragen können.

Nunmehr stand wieder ein Neuanfang auf dem Programm – von Zeit zu Zeit sollte jede Homepage mal einem Review und ggf. auch einer Modernisierung unterzogen werden. Wie sich zeigte war Joomla! nicht mehr die absolute Präferenz, zumal sich nun auch Personen mit deutlich besseren Kenntnissen in Sachen Layout und Gestaltung gefunden hatten. Die neue Homepage soll unter Typo3 entstehen. Mir ist das System letztlich egal – jeder soll das System verwenden mit dem er am Besten arbeiten kann. Nicht jedes Werkzeug ist automatisch für jeden Zweck geeignet nur weil viele es für diesen einsetzen.

Für mich als Systemadmin ändert sich durch den Wechsel des CMS eigentlich nichts – die Anforderungen sind recht human: Apache als Webserver (es gingen wohl auch einige andere), MySQL bzw. MariaDB als Datenbank und PHP als Skriptsprache die das ganze zusammen hält. Soweit nichts außergewöhnliches.

Schon interessanter werden da diverse nette Features die Typo3 so mitbringt, wie etwa das automatische Skalieren von Bildern oder auch die Erzeugung von PDFs. Alles Funktionen die gut ankommen wenn man sie richtig einsetzt. Für die ganze Bildbearbeitung kommt ein mir nicht unbekanntes Tool zum Einsatz ImageMagick – diese in C geschriebene Toolbox ist das Schweizer Taschenmesser was automatisierte Bildbearbeitung betrifft – die Bedienung der Tools auf der Kommandozeile scheint anfänglich etwas kryptisch, aber gerade für Stapelaufgaben ist sie hervorragend geeignet. Die Libary dazu habe ich schon länger auf meinem Server installiert – für meine Webprojekte verwende ich direkt die PHP-Schnittstelle, die auf den Namen Imagick hört. Diese ermöglicht unter anderem die Berechnung von Thumbnails on demand, und ist dabei dank C-Implementierung um ein vielfaches schneller als die GDLib, die sonst in PHP für die Bearbeitung von Bildern verwendet wird. Ich selbst habe Imagick schon für die von mir betreute Website der THW-Jugend in Mannheim verwendet – irgendwann war es mir einfach lästig geworden jedes Mal für eine Bildergalerie die Thumbnails von Hand zu berechnen – mit ein klein wenig Datenbank-Background habe ich das dann aus der Welt geschafft. Klingt zwar im ersten Moment etwas schräg ausgerechnet Bilder als Binary large Object in eine Datenbank zu packen, aber die Optionen sind einfach zu schön. Mal ganz davon abgesehen, dass man sich nicht mehr um irgendwelche Dateirechte in shared hosting Umgebungen Gedanken machen muss – auch etwas wie Schlagworte oder Gruppierungen sind in einer relationalen Datenbank einfach wesentlich leichter möglich, als wenn alles im Dateisystem liegt.

Typo3 geht einen etwas anderen, universelleren Weg und ruft das Tool “convert” aus dem Imagick-Paket auf – das macht es mächtiger aber in meinen Augen auch umständlicher zu bedienen. Jedenfalls ist die Installation auch kein Hexenwerk. Paketverwaltung auf – installieren, ja, Installation fertig. Soweit mal die Vorbereitung.

Die Rückmeldung lässt nicht lange auf sich warten: ImageMagick funktioniert generell, aber wir haben ein Problem mit der Version … der Kunde wünscht in diesem Fall eine ältere Version – was mir als Betreiber natürlich etwas widerstrebt, gibt es doch zahlreiche Bugfixes auf die ich selbst angewiesen bin und diverse kleinere Lücken im Code, die geschlossen wurden.

Nach etlichem Hin und Her ist das Problem zumindest einmal eingegrenzt: Wann immer man mit Typo3 versucht eine Adobe Illustrator-Datei in ein JPEG zu überführen, knirscht es bezüglich der verwendeten Transparenz. Der Hintergrund wird nicht weiß sondern schwarz dargestellt, was gerade beim Weiterverwenden in PDFs nicht gut aussieht.

In diversen Foren ist das Problem auch beschrieben und es gibt diverse Abhilfe Vorschläge – unter anderem ein Downgrade, was für mich ja wie geschrieben eigentlich nicht in Frage kommt. Also geht die Suche nach der Ursache weiter – ich bin ja sogar Willens mich ggf. mit dem Code von Image Magick auseinander zu setzen – Bildbearbeitungsalgorithmen habe ich zwar mal gelernt, aber so schwer kann es in dem Fall ja nicht werden.

Über mehrere Zwischenstufen kristallisiert sich für mich langsam die Struktur von ImageMagick heraus – mit Nichten ist das eine allwissende Bibliothek mit angeschlossenem Werkzeugkasten, wie ich lange Zeit gedacht hatte. Vielmehr lagert ImageMagick diverse Aufgaben an dezidierte Programme aus – was ja auch sinnvoll ist, man braucht das Rad ja nicht zweimal zu erfinden und nirgendwo ist die Wiederverwendung von Programmen und Code so einfach wie in Software und gerade im OpenSource-Bereich.

Ich lerne Adobe Illustrator Dateien sind nur verkappte PDFs mit einigen Erweiterungen – zum Lesen eignet sich das vielseitige Ghostscript, das ursprünglich mal als PostScript-Interpreter gestartet ist. Man muss wissen, dass PDF anfänglich auch nur eine etwas aufgebohrte Version von PostScript war. PostScript war lange Zeit die Sprache um Drucker sauber und effizient anzusteuern – ein guter Netzwerkdrucker versteht sich zur Freude von Linux-Anwendern und Mac-Nutzern auch heute noch auf PostScript. Die Logik zur Aufbereitung von Dokumenten und Druckvorbereitung lag damals schon im Drucker. Erst mit GDI und PCL gab es die ersten “Thin”-Printer – also Drucker ohne große Eigenintelligenz die vom Rechner “ferngesteuert” wurden. PDF war der Schritt vom Papier in die digitale Welt – also eine Art unabhängiges Dokumentenformat, das sich aber auch auf Referenzen verstand.

Halten wir also fest Typo3 ruft ImageMagick bzw. dessen Tool convert mit einigen Parametern auf, das wiederum übergibt die Sache zumindest teilweise an Ghostscript. Was genau wohin übergeben wird, steht in der

delegates.xml

– wo genau die sich findet hängt ein wenig von der Distribution ab – unter Ubuntu findet man sie unter [code]/etc/ImageMagick/delegates.xml[/code].

Einer der Tipps aus den Foren lautet den vollständigen Pfad für ghostscript gs  anzugeben, das betrifft aber im Wesentlichen die Windows-Welt – schaden kann es ja mal nichts, aber leider bleibt mir der Erfolg versagt. Das Problem besteht weiterhin – die Hintergründe werden schwarz anstelle transparent (also weiß) dem auf Papier (es sei denn man druckt gerne auf schwarz, wobei dann die meisten Drucker mit ihren Farben an die Grenzen des Möglichen kommen – weiß als Tinte oder Toner habe ich noch bei den wenigsten Druckern gesehen).

Nächster Schritt in der Fehlersuche: Handarbeit: Also einmal ein Bild von Hand auf der Kommandozeile mit convert umwandeln – die Befehlszeile dazu lautet anhand von Typo3:

convert' +profile '*' -geometry 170x170! -colorspace RGB -quality 70  'typo3logotype.ai'[0] 'install_read_ai.jpg'

Was mir auffällt: Das Ausgabe-Format ist JPEG – wie ich weiß und mich auch nochmals informiere unterstützt JPEG keine Transparenz (dafür gibt es andere praktische Formate wie PNG oder früher auf GIF) – indem ich mir ein PNG ausgeben lasse, kann ich das Problem wieder ein Stück herunter brechen: Das Einlesen funktioniert wohl sauber, denn das ausgegebene Bild enthält korrekt weiterhin die Transparenz – ergo muss irgendwas bei der Umwandlung in JPEG schiefgehen.

Die Lösung des Problems ist recht simpel:

-background white -flatten

in die Angabe eingebaut und schon kommt das passende Ergebnis bei heraus – es geht unter anderem natürlich auch mit anderen Farben als Hintergrund.

Nun ist eine Lösung an sich ja schon mal was tolles, aber ich will ja wissen warum es früher mal so funktioniert hat und jetzt nicht mehr. Wie schon vermutet hat sich in der neuen Version von ImageMagick etwas bei JPEG schreiben verändert. Die Funktion zum “plattklopfen” bzw. dem Zusammenführen von Ebenen aus einen konstruierten Bild wird jetzt nicht mehr implizit aufgerufen. Bei vorherigen Versionen war dies der Fall wenn Transparenz vorlag und ein Hintergrund gegeben wurde. So eine simple und auch gut nachvollziehbare Änderung (jede Funktion sollte das machen was drauf steht, das richtig, aber auch bitte nicht noch mehr), hat mich hier eine ganze Weile auf Trab gehalten.

Wenn man den Aufruf in der Typo3-Konfiguration anpasst, dann klappt auch die Umwandlung ohne Probleme. Nur schade, dass diese so simple Lösung (die noch dazu auch mit älteren Versionen von ImageMagick richtige Ergebnisse liefert) den Weg noch nicht in die aktuelle Fassung von Typo3 gefunden hat. Wollen wir hoffen, das dies bald der Fall ist.

Quick’n’Dirty in MySQL und anderen Datenbanken

Heute habe ich mich mal wieder einer Altlast der Datenbankentwicklung hingegeben, da sich einige Veränderungen ergeben hatten. Ich hatte schon mehrfach, die Hoffnung diese Tabellen der Datenbank endlich einmal längere Zeit in Ruhe lassen zu können um mich neuen Funktionen zu widmen – aber Pustekuchen wars.

Also wieder das Design auf den Prüfstand und schauen wie man es an die neuen Anforderungen anpassen kann. Ich weiß, dass ich vor etwa einem halben Jahr noch mit einem Freund und ausgesprochenen Experten in Sachen Datenbankdesign mich über einige Dinge ausgetauscht habe. An einigen Stellen hatte ich mich für geschickt gehalten bzw. wollte an dem Design nicht mehr übermäßig rütteln. Es hat ja auch alles soweit funktioniert und gerade die Schlüsseldefinitionen folgten auch einer gewissen Logik. Ich hatte mich für einen kombinierten Schlüssel entschieden – ein referenziertes Objekt kann zu einem Zeitpunkt (auf die Sekunde genau) nur an einer Stelle sein – für den Anwendungsfall eine absolut zutreffende Annahme. Zudem hatte ich den Schlüssel dann auch noch über mehrere Tabellen als Fremdschlüssel “durchgeschleift” – vom damaligen Standpunkt aus war das eine mögliche Lösung die mir eigentlich auch gut gefiel – löste sie doch elegant auch diverse Bezugsprobleme, bzw. ich konnte einen Trigger verwenden um die notwendige Abhängigkeit einer Tabelle von einer anderen automatisch aufzulösen. Es gab also die Basis-Tabelle, eine erweiterte Tabelle und eine Tabelle die in vergleichsweise wenigen Fälle sich auf die erweiterte Tabelle stützte – ein klassisches Prozessgefälle – aus vielen kleinen Datensätzen werden am Ende nur wenige bis zur Blüte oder gar Reife gebracht.

Nun, die Anforderungen haben sich verschoben und die  mittlere/erweiterte Tabelle musste angepasst werden. Wie sich gezeigt hatte brauchten wir für eine spezielle Auswertung nicht nur eine Referenz auf die Basis-Tabelle sondern mindestens zwei, nach eingehender Analyse bin ich auf vier gekommen. Dies liegt in der Tatsache begründet, dass die erweiterte Tabelle eigentlich ein Zusammentreffen mehrer Datensätze aus der Basis-Tabelle abbildet. Das ist mir aber erst im Laufe der weiteren Entwicklung klar geworden – ich denke ich habe das auch beim letzten Mal eher “on the fly”,”mal eben schnell”, “quick’n’dirty” entwickelt ohne die wahren Beziehungen zu erkennen. Was will man machen – so manches wird einem eben erst im Laufe der Zeit klar.

Erste Konsequenz – der ursprünglich ach so geschickte natürliche Schlüssel über zwei Spalten war nun nicht mehr tragbar – viel zu umständlich: für vier mögliche Referenzen wären es acht Spalten gewesen – Übersichtlichkeit gegen null, zumal die Aussagekraft der jeweiligen Schlüsselpaare zum Gesamtbild nur vergleichsweise wenig beiträgt.- und selbst wenn man es braucht – gejoint ist es dank Indizierung und Foreign Keys doch recht fix. Daher bekommt die Basis-Tabelle neben den natürlichen Spalten ein Surrogat – einen eindeutigen numerischen Primärschlüssel. Wie leicht der einem die Arbeit im weiteren macht ist mir bei der Anpassung des Programmcodes dann aufgefallen.

Wie mit der erweiterten, nunmehr ja eher aggregierenden Tabelle weiter verfahren – außer den vier Spalten für die Referenz – ein natürlicher Primärschlüssel über vier Felder schien mir doch recht gewagt, zumal diese Referenzen sich auch mal im Nachinein noch verändern können. Also auch hier die “künstliche” Variante mit einem Surrogat.Das entschlackt auch die letzte Tabelle in der Reihe – deren Referenz musste ja auch wieder irgendwie hergestellt werden – nachdem der ursprünglich “durchgereichte” Schlüssel ja nicht mehr da war musste da eh etwas neues her – auch hier erweist sich die Lösung per Surrogat doch recht tauglich.

Lehrwerte dieser Aktion:

Erstens – natürliche Schlüssel haben einen gewissen Charme – auch wenn sie zur Not aus zwei Spalten bestehen – moderne Datenbank-Systeme stecken das recht gut weg, auch was die Performance betrifft.

Zweitens – eine sorgfältige Analyse und Diskussion eines Entwurfs und die Bedeutung eines Objekts im Gesamtzusammenhang ist durch nichts zu ersetzen – leider zeigt sich hier mal wieder, dass es in meinem Fall keinerlei Prozessdefinition gab und somit natürlich auch die Artefakte nur sehr lückenhaft beschrieben waren. Ein Pflichtenheft wurde aus Kostengründen auch nicht erstellt – stattdessen gab es eine Alt-Datenbank an der man sich orientieren sollte – in bestimmten Dingen war das Design eine Anleitung “wie man es tunlichst nicht machen sollte” (bei Gelegenheit werde ich dazu mal noch ein paar Zeilen schreiben). Auf einem solchen weichen Untergrund ein solides Fundament und hinterher ein Gebäude zu errichten ist nahezu unmöglich – irgendwo sackt es am Ende doch unangenehm weg.

Drittens – Surrogate sind im ersten Moment oftmals hinderlich und an einigen Stellen “verstellen” sie teilweise den Blick aufs Wesentliche – man muss sich ggf. die weiterführenden Informationen aus anderen Tabellen erst mal zusammen suchen. Aber sie haben auch eine Menge Vorteile in Sachen Eindeutigkeit und Handhabbarkeit – wenn es einen eindeutigen Wert gibt, erleichtert dass das Auffinden eines Datensatzes und das Instanzieren eines Objekts daraus ganz erheblich.

Mal sehen welche alten Entscheidungen ich demnächst wieder ausgraben muss und mich über meine eigene Schusseligkeit wundern/ärgern darf. In diesem Sinne: Augen auf beim Datenbank-Design.

MySQL und der Null-Pointer

Da denkt man mal wieder an nichts Böses und prompt holt einen das 2. Gebot der C-Programmierung ein – obwohl man nicht in C programmiert, sondern eigentlich nur in einer Datenbank unterwegs ist.

Die 10 Gebote finden sich hier:

http://www.geekhideout.com/c-ten-commandments.shtml

Und die Aussage “Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.”  ist mir dabei mal wieder wie Schuppen von den Augen gefallen.

Was war passiert? Ich habe eine Datenbank-Tabelle, die mir angibt welche Subunternehmen an einer Arbeit mit beteiligt werden: Pro Arbeit kann es mehrere Subunternehmer geben, und jeder Subunternehmer kann an beliebig vielen Arbeiten mitwirken. Soweit so gut, ein klassischer Fall einer n:m Beziehung (wie man das im ER-Modell bezeichnen würde, UML ist da etwas genauer und würde in diesem Fall von einer 0..n:0..m Beziehung sprechen). Nun habe ich feststellen müssen: Selbst wenn das offiziell nur ein Subunternehmen ist, dann kann das immer noch recht groß ausfallen – die reine Angabe einer Firma als Referenz reicht also nicht unbedingt aus. Soweit ja kein Problem: Es gibt ja auch noch eine Tabelle aller am Projekt beteiligten Mitarbeiter – und ggf. ist einer davon als Ansprechpartner definiert. Leider nur ggf. und nicht zwingend – bei kleineren Firmen gibt es eine solche Zuweisung leider nicht.

Nun ja, was macht der geneigte Datenbank-Programmierer, genau, er macht das was er mal gelernt hat: Wenn es keine Referenz gibt, dann tragen wir den speziellen Wert “NULL” ein. Selbst die referentielle Integrität kommt damit nicht aus dem Tritt – vielmehr ist es ein ganz klares Zeichen: Hier muss gar nicht erst nach einem passenden Partner in der anderen Tabelle gesucht werden – es gibt ihn schlichtweg nicht. Alles nicht richtig spannend und für mich bis dato kalter Kaffee.

Spannend wurde es im Zusammenhang mit der Modifikation der Datensätze: Um zu verhindern das Doppelte Einträge vorhanden sind, habe ich einen Unique-Key über die drei Spalten “Arbeit”,”Firma”,”Ansprechpartner” angelegt. Zudem enthält die Tabelle noch ein paar zusätzliche Information, die aber nichts zu Sache tun. Was liegt also näher als mit einem “Insert ignore”, “Upsert” bzw. dem praktischen MySQL-Konstrukt “Insert into …. on duplicate key update ….”  zu arbeiten? Das ist im Prinzip wie geschaffen für den beschriebenen Fall: Gibt es schon einen entsprechenden Eintrag in den  Schlüsselspalten, dann muss man nur noch die Satellitendaten entsprechend dem Bedarf aktualisieren (das lässt z.B. gut Luft für eine Überwachung durch wen und wann die letzte Änderung durchgeführt wurde).

Doch was ist das: Beim Testen der Funktion tauchen nach dem Speichern der Modifikation plötzlich Einträge doppelt und dreifach auf – also gerade nicht das was man erwartet hätte.

Erster Schritt in der Fehlersuche: Irgendwas im Code wurmig? – Nein, da stimmt alles und es gibt auch keine enstsprechenden Extra-Inserts – die Statements sehen so aus wie man sich das gewünscht hat. Nächster Schritt: Statements “manuell” ausführen und … aha! – man kann Einträge bei denen der Ansprechpartner auf “NULL” steht beliebig oft einfügen ohne dass die Schlüsselmechanismen ansprechen…. erster Verdacht: MySQL-Bug! – aber weit gefehlt: “It’s not a bug, it’s a feature” zumindest in den Augen der MySQL-Entwickler … aber Abhilfe gibt es auch: einfach einen “primary” anstelle eines “unique” keys nehmen…. gesagt getan: Aber dann hagelts erst richtig: Denn in einem “Primary”-Key dürfen keine “NULL”-Werte auftauchen – was macht MySQL intern: Man behilft sich, und verändert sie Spalte – sie darf jetzt nicht “NULL” enthalten und der Default-Wert ist “0” – und das trotz eines Fremdschlüssels? – Und genau da beißt sich die Schlange dann in den Hintern … denn die bestehenden Daten sind komischerweise “valide” – keine Schlüsselverletzung. Aber Einfügen geht nicht mehr: Will man “NULL” einfügen sperrt sich die Tabellendefinition, will man “0” verwenden, gibt es von Seiten der referentiellen Integrität auf die Finger … So ein Mist!

Lösungen gibt es bisher dafür keine 😐 zumindest nicht auf Datenbank-Ebene. Ich habe jetzt einen Hack-Around gemacht: Man lösche alle Einträge zur Arbeit und lege sie danach alle wieder an und schon kann man mit dem Unique-Key leben, man hat praktisch die Prüfung etwas weiter nach oben verlagert (wo sie in meinen Augen nicht hingehört).

Aber wie schon da Commandement von oben sagt: Null-Pointer sind ganz böse Dinge – und wenn man nicht aufpasst findet man sich in der Hölle wieder 😉

 

PHP, Imagick und Windows

Für heute hatte ich mir ein weiteres komplexeres Thema unserer Datenbank auf Arbeit vorgenommen:
An vielen Stellen benötigen wir externe Dateien – sei es um Handbücher schnell und einfach greifbar zu haben, Bilder von einem Schaden oder auch nur zur allgemein zu einem Bauteil ablegen zu können.
Eine Option wäre die Ablage als Datei im Dateisystem des Webservers – das funktioniert, aber es ist schwierig Meta-Informationen wie den Titel abzulegen. Für Bilder ist es oftmals hilfreich eine Ansicht zu haben, die nicht gleich das vollständige Bild sondern nur eine Vorschau zeigt – sei es als originäres Thumbnail oder auch nur in einer Größe die nicht gleich den Bildschirm sprengt, sondern sich in die Seite nach Bedarf einpasst.
Und was auch ganz praktisch wäre – wenn schon etwas gelöscht wird, sollten vielleicht auch die Leichen in Form von nicht mehr benötigten Dateien verschwinden.

Für die THW-Website, welche ich eine zeitlang betreut habe, hatte ich sowas schon einmal realisiert, allerdings war das technisch nicht so ganz ausgereift und ich wollte es wenn dann gleich auch noch technisch sauber bekommen – wenn man schonmal dabei ist. Eine Anpassung war ohnehin notwendig, denn es geht jetzt nicht mehr nur um Bilder sondern um Dateien und Dokumente in jeglicher Form: Also auch PDFs, Excel-Tabellen, und diverse andere Dinge die da so anfallen können.

Aber das Basis-Wissen war ja glücklicherweise schon da. Nach ein wenig Bastelei und Anpassungen an der Datenbank-Struktur war der Code dann auch soweit lauffähig. Mit dem kleinen Problem, dass ich bisher auf meinem Entwicklungsrechner kein Imagick installiert hatte.
Was zum …. ist Imagick? – Imagick ist eine recht umfangreiche und mächtige Bibliothek zur Bildbearbeitung, die auch recht zügig arbeitet. Unter anderem kann sie Bild-Dateien in der Auflösung reduzieren und bietet praktische Möglichkeiten um Bilder zu bearbeiten (Stichwort: Wasserzeichen, Copyright-Vermerke etc.).

Von meiner Linux-Umgebung bin ich ja schon etwas verwöhnt – “apt-get install xyz” hilft in den meisten Fällen und wenn nicht hat man die notwendigen Erweiterungen dank PECL und PEAR auch recht schnell nachinstalliert. Unter Windows gestaltet sich das nicht ganz so einfach – vor allem die Fehlersuche ist mühsam und besteht aus jeder Menge Trial-Error.

Auf Anhieb tut mal gar nichts – auch nicht nach der Installation der aktuellen Imagick-Version. Ständig bekomme ich beim Serverstart die Meldung “Modul kann nicht gefunden werden” – sehr aussagekräftig. Wenn man dann mal ein wenig sucht, findet man, im Englischen heißt die Fehlermeldung “could not find procedure” – das gibt immerhin schonmal einen besseren Anhaltspunkt wo der Fehler sein könnte – es mangelt wohl nicht an der DLL-Datei sondern an der von dort referenzierten Library in Imagick.

Was dann noch erwschwerend hinzu kommt: Es gibt mehrere Versionen und Compilate der DLL für Windows:
thread-safe, non-thread-safe und einmal mit Visual C 6 und Visual C9 kompiliert (entsprechend den Versionen von Visualstudio) kompatibel sind die natürlich nicht :-O

Es hat mich ziemlich viel Nerven gekostet, aber hier habe ich jetzt eine Lösung die mit folgender Kombination funktioniert:
– WAMP-Server 2.1
– Apache 2.2.17
– PHP 5.3.5
– ImageMagick 6.6.9-7

Hier die Anleitung:

1.) ImageMagick installieren und zwar genau diese Version hier:
http://www.imagemagick.org/download/binaries/ImageMagick-6.6.9-7-Q16-windows-dll.exe
Das ist die dynamisch gelinkte Version mit 16 Bit Qualität pro Subpixel und inklusive der notwendigen DLLs
2.) Eine Umgebungsvariable anlegen die auf das Installationsverzeichnis verweist: MAGICK_HOME=c:\Programme\Imagick-xyz\
3.) Neustart machen (warum Windows das braucht – keine Ahnung aber erst dann rafft es die Kiste, dass es da überhaupt Libraries für gibt – und die Fehlermeldungen werden klarer)
4.) WAMP 2.1 ist mit VC6 kompiliert, also brauchen wir eine entsprechende Version der Extension DLL für PHP – sonst gibt es Fehlermeldungen der Art:
PHP-Build-Version = “Zahlenkombination”
Module-Build Version = “Zahlenkombination”
Those values need to match
Die passende Version für unseren Fall findet sich hier:
[url]http://www.sk89q.com/downloads/imagick/2.3.0/API20090626-TS-VC6/php_imagick.dll[/url]
5.) Datei herunterladen und im WAMP-Verzeichnis\PHP\5.3.5\ext\ ablegen
6.) PHP.ini anpassen und die Extension in die Liste eintragen
7.) Server neu starten – es sollte diesmal keine Fehlermeldung geben
8.) Geschafft und man kann sich den eigentlichen Problemen zuwenden!

Ergo: Es ist verdammt aufwändig Erweiterungen für PHP unter Windows einzubauen – die verschiedenen Versionen machen es einem ebensowenig leicht wie die teils wenig hilfreichen Fehlermeldungen und die Tatsache das so Dinge wie der Neustart in keiner Dokumentation auftauchen.

Dank an all die verschiedenen Seiten und Leute die sich ebenfalls schon mit dem Problem beschäftigt haben und Tipps zur Lösung geben haben:

[list]
[*][url]http://www.blogix.net/2011/04/03/imagemagick-xampp-windows-installieren/[/url]
[*][url]http://www.sk89q.com/2010/03/vc6-windows-binaries-for-imagick-2-3-0/[/url]
[*][url]http://www.knowledge-transfers.com/it/installing-imagemagick-on-windows-setup-imagick-on-php/[/url]
[*][url]http://valokuva.org/?page_id=50&cpage=7#comments[/url]
[/list]

Schade, dass es so aufwändig ist eine derart praktsiche Bibliothek unter Windows einzusetzen – wie gesagt unter Linux ist sie hervorragend gepflegt.

Posted in Web |