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

/etc/ImageMagick/delegates.xml

.

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.

Never touch a running system

Das hat unsere IT-Abteilung für die Versorgung mit Rechnern doch wieder mal exakt hinbekommen. Mein Kollege bekommt pünktlich zum Beginn meines Urlaubs einen neuen Rechner … Dabei habe ich doch gerade auch einen Praktikanten, den ich auch noch einweisen muss. Meine eigentliche Arbeit habe ich daher schon mal ganz hinten angestellt – vor dem Urlaub wird es wohl nichts mehr mit der Fertigstellung.

Insgesamt bin ich aber doch recht angetan von unserem Praktikanten – nach nicht mal zwei Wochen hat er jetzt ein einfaches Modul übernommen – ich bin mal gespannt wie sich das entwickelt während ich in Urlaub bin. Noch bin ich da recht zuversichtlich. Das da die eine oder andere Frage auftaucht, gerade am Anfang ist eine ganz natürliche Sache – jeder hat mal klein angefangen und da gibt es deutlich unselbstständigere Mitarbeiter.

Die größere Herausforderung war da der Rechner des Kollegen – da muss nämlich alles was Entwicklungstools heißt dann wieder neu installiert werden. Die werden nämlich nicht migriert – stattdessen gibt es neue Versionen von Notes und ein neues Office (in beiden findet man mal wieder nicht das was man eigentlich sucht …)

Besonders läßtig sind dann wieder die Spielchen mit WAMP und der Entwicklungsumgebung – Eclipse an sich ist ja noch harmlos – aber die ganzen Plugins sind dann schon wieder eine Herausforderung. Zumal man ja die Chance auch nutzt und dann doch mal endlich ein Upgrade auf die aktuelle Version machen will. Abgehangene Tools sind doch eher nicht so der Hit. Auch ich habe da wieder etwas dazu gelernt und meine internen Versionszähler mal aktulisiert. Mittlerweile ist Eclipse ja bei 4.2 aka Juno angekommen – ich arbeite noch mit Helios also der 3.6er Version – da wäre auch bei mir mal ein Upgrade angebracht … Immerhin habe ich jetzt auch endlich mal die Zeit gefunden mich mit den verschiedenen Konzepten etwas näher auseinander zu setzen – gut wenn man sein Werkzeug besser kennen lernt.

Subclipse hat sich auch weiter entwickelt – genauso wie Subversion selbst – das hatte ich noch so nebenher mitbekommen.

Insgesamt war ich dann doch überrascht wie gut das alles zu installieren war.

Das dicke Ende war dann mein spezieller Freund: Die Extensions für WAMP bzw. PHP – da gibt es dann wieder x Varianten und inkompatible Kompiler von Microsoft gleich en top mit dazu. Noch dazu, ganz wichtig für die Installation von Libraries unter Windows: Ein Neustart – und das für dynamische Libraries – was soll daran bitte dynamisch sein? Das hat mich mal wieder einiges an Nerven und Zeit gekostet – zumal es mal wieder so absolut unintuitiv ist ….

Morgen geht es noch ein wenig an weitere Plugins, dann haben wir es hoffentlich passend zum Urlaub geschafft …

 

Programmier-Ärger mit VBA

Da hab ich doch heute mal wieder ein tolles Projekt „geerbt“ – für eine Tauschaktion sind in größerer Menge Serienummern-Etiketten zu erstellen. An und für sich ja eine recht einfache Aufgabe.

Trickreich wird sie dadurch, dass ein bestimmtes Muster eingehalten werden muss und auch die Nummern noch in verschiedenen Farben gedruckt werden sollen. Hintergrund der Geschichte ist: Damit es weniger Probleme mit dem Einlesen/Abschreiben von Seriennummern gibt, erhält der Mitarbeiter einen vorgedruckten Bogen mit Seriennummern, jede Nummer ist in leicht anderer farblicher Schattierung doppelt vorhanden: Eine für auf das Gerät, eine für ins Protokoll. Pro getauschten Teil sind es also insgesamt 4 Nummern – damit man sich nicht so leicht vertut sind die alten und neue Geräte-Kleber auch noch jeweils in stark unterschiedlichen Farben gehalten.

Auch die Größe der Etiketten steht bereits fest: Avery Zweckform 3666 – 65 kleine Klebeschildchen auf einem Bogen A4, je 21,2mm hoch und 38mm breit. Weitere Anforderung an das Hilfsmittel: Farben bitte einstellbar falls noch etwas anderes benötigt wird – und die Startnummer sollte man auch noch festlegen können, damit es keine doppelten Seriennummern gibt.

Immerhin man hat schon mal etwas vorbereitet: Eine Excel-Datei mit   einem Bogen und einer Startnummer zum eingeben – quick&dirty aber es tut. Problematisch: Man kann immer nur 32 Nummern damit erzeugen, dann ausdrucken und die nächste. Bei mehr als 1000 Seriennummern die benötigt werden sicherlich nicht das optimale Tool.

Die ersten Hürden sind schnell genommen: Die notwendige Schriftart auf dem Rechner installiert damit man Barcodes erzeugen kann ist doch schon mal die halbe Miete, dann gibt es nämlich keine problemtischen Schriftzeichen mehr sondenrn tatsächlich passende Barcodes.

Die eigentliche Freude beginnt danach – da es ja schon etwas gibt versucht man natürlich die bestehende Datei weiter zu nutzen – leider ein Schuss in den Ofen – denn die Felder sind alle „hardcoded“ als Referenz in den einzelnen Excel-Feldern hinterlegt.

Gehen wir das ganze also doch programmatisch an – erst mal trenne ich die Eingabe von der Ausgabe ab, damit nicht irgendwelche unnötigen Zeilen und Spalten herumschwirren die hinterher den Ausdruck schwierig machen.

Danach beginnt die eigentliche Kür – VBA-Programmierung, lange ist es her, dass ich damit gearbeitet habe – und um so kruder wird der Einstieg. Immerhin es gibt recht viele nette Beispiele im Netz, anhand derer kann ich mich langsam vortasten.

In einem ersten Schritt erzeuge ich mir das „Raster“ für die Etiketten  automatisiert – leichter gesagt als getan – was man in Excel sonst mit der Maus einfach mal „hinlupft“ funktioniert natürlich nicht mehr, wenn man es sauber programmiert haben will – unter anderem muss man daran denken, dass ein Etikett rechts und links einen Rand bekommt und nicht nur einfach einen Abstand zwischen zwei Barcodes hingemurkst wird. Schon bei der Aktion fallen mir die kruden Werte auf mit denen Excel hantiert – eine vernünftige „Maß-Einheit“ ist leider nicht zu erkennen. Aber egal – die ungefähren Werte wurden ja schon empirisch ermittelt.

Insgesamt finde ich die Programmierung sehr holprig – vor allem der eingebaute Editor in Excel nervt mit seiner permanenten Kontrolle und Fehlermeldungen – ein Stück Code von woanders her kopieren – nicht möglich wenn in der aktuellen Zeile noch ein Fehler ist. Ich bin ja für Fehlerhinweise dankbar, aber so aufdringlich muss es doch nun wirklich nicht sein – selbst PHP-Skripte und Eclipse beschweren sich nicht derart – spätestens wenns ans Laufenlassen geht, aber dann ist der Fehler ja auch gerechtfertigt wenn einer drin ist.

In die selbe Richtung gehen die Fehlermeldungen zur Laufzeit: „Objektinterner Fehler“ ist eine sehr aussagekräftige Fehlermeldung – mit ein wenig Recherche im Code kommt man dann darauf, dass ein negativer Index das Problem sein könnte…

Die eigentliche algorithmische Aufgabe liegt dann darin, die Verteilung der Nummern auf die Seite zu berechnen – in gewisser Weise hat das etwas von Speicherzugriffsberechnungen aus der  technischen Informatik. Lästig sind dabei zwei Dinge: Excel bzw. VBA indiziert nicht wie gewöhnliche Programmiersprachen das tun ab 0, sondern weil es ja so viel besser ist beginnen alle Indizes mit 1 … und genauso intelligent verhält es sich bei Ganzzahlen: Wenn man dividiert wird automatisch korrekt gerundet – das gibt natürlich bei der Reihen und Spaltenberechnung ziemliches Chaos wenn man über die Hälfte eines Blattes hinaus ist.

Zusätzliche Nettigkeit der Sache an sich: 32 Nummern, also insgesamt 64 Etiketten passen ja nicht ohne weiteres auf 65 Felder – eines muss frei bleiben. Das wäre noch halb so wild – aber die Aufteilung 13×5 passt natürlich wie faust auf Auge zu einer geraden Anzahl Nummern. Also wird in der untersten Reihe nicht untereinander sondern nebeneinander gedruckt.

Auch total ungewohnt für mich: Das zusammenführen von Strings oder Zahlen – in PHP ist es der Punkt zum Konkatenieren, in vielen anderen Sprachen ist es das „+“ (vor allem wenn es stark typisierende Sprachen mit überladenen Operatoren sind) in VBA ist man den Sonderweg des „&“ gegangen – wer auch immer sich das hat einfallen lassen.

Das Einfärben der entsprechenden Bereich anhand eines vorgegeben beschäftigt mich auch noch mal etwas – die Angaben finden sich natürlich wieder da wo man sie nicht vermutet – wer sucht denn bitte die Hintergrundfarbe einer Zelle in Cell.Interior.Color und die Farbe in Cell.Font.Color? Ganz zu schweigen von etwas wie Fett oder kursiv – das wird nicht per Style festgelegt sondern per true oder false … ganz merkwürdige Sache, wenn man gewohnt ist mit HTML und CSS zu arbeiten.

Am Ende habe ich dann immerhin eine Lösung die funktioniert – die Performance empfinde ich persönlich als „lausig“, man muss Ewigkeiten warten bis die Seite endlich fertig aufgebaut ist, und das selbst wenn man nur „200“ Seriennummern benötigt. Zusätzliche Schwierigkeiten bereiten mir dann noch die Maßeinheiten – damit es auf dem Drucker aus passt ohne dass die Schilder aus dem Passer laufen … Interessant wo die krummen Werte für die Umrechnung herkommen – die sind weder metrisch noch Zoll – wahrscheinlich eine interne Microsoft-Spezial-Längeneineheit, das würde dann auch erklären warum sie in der Breite anders umgerechnet wird als in der Höhe …

Ich denke ich werde mir den Spaß nochmal erlauben, es doch in PHP zu schreiben und daraus direkt ein PDF zu erzeugen – die Hauptarbeit mit der Zeilen und Spaltenberechnung ist ja schon gemacht….

Aber ich weiß mal wieder warum ich doch „echte“ Programmiersprachen einer Markosprache einer Office-Suite vorziehe – eine Entwicklungsumgebung kann Office einfach nicht ersetzen.

 

 

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 😉

 

MySQL InnoDB und das Ende der Welt

Wer erinnert sich noch an den Hype um das Jahr 2000-Problem – sicherlich einige. Mittlerweile schreiben wir das Jahr 2012 und alles scheint in bester Ordnung. Scheint – die Realität sieht manchmal leider etwas anders aus.

Alles beginnt mit ganz harmlosen Sympthomen: Ein Mitarbeiter meldet sich bei mir, er könne in der von mir betreuten Datenbank keine weiteren Datensätze anlegen. Die Fehlermeldung besagt, dass eine Schlüsselverletzung vorliegt.

Erster Ansatz: Nachschauen wo man mal wieder etwas zu restriktiv im Datenmodell oder irgendwo etwas zu simpel im Code gearbeitet hat. Aber leider: Fehlanzeige – nichts offensichtliches zu entdecken.

Nächster Versuch: Man füge einen Datensatz direkt auf SQL-Ebene ein – wichtig dabei, man halte sich so weit wie möglich an das Original beim Einfügen. Also lässt man ganz bewusst das Feld für den Primär-Schlüssel leer – steht ja auf auto_increment und damit meint man macht MySQL doch in der Regel keine Zicken.

Aber: Holla! Was ist denn das? MySQL weigert sich den Datensatz einzufügen, angeblich wegen einer Verletzung des Primär-Schlüssels!

Ein wenig Recherche später findet man dann denn auch die Ursache: Der Zahlenbereich des verwendeten Zahlentyps war am Ende. Nun dachte ich eigentlich, dass MySQL dann hergehen würde und sich irgendwie die nächstbeste freie Zahl nimmt – von mir aus ja auch negativ – ist ja kein Drama. Platz war definitiv noch – denn auch wenn in der Tabelle rund 150.000 Datensätze gespeichert waren, so ist das noch immer deutlich kleiner als die 2147483647 möglichen positiven IDs die ein 32 Bit signed Integer in MySQL zur Verfügung stellt (doppelt so viele nochmal wenn man die negativen Zahlen mitberücksichtigt).

Hotfix in dieser Situation war eigentlich recht einfach: von allen vorhandenen IDs eine möglichst große Zahl abziehen, unter Berücksichtigung der Einschränkung, dass keine Id auf den Spezialwert 0 fallen darf. Einfacher gesagt denn getan – die Tabelle repräsentiert nämlich eigentlich eine Baumstruktur mit einem Verweis auf den Vater der jeweiligen Zeile. Da kommt MySQL mit dem Update leider nicht klar, denn aufgrund der fehlenden Bedingung schnappt sich MySQL einfach einen Lock auf die gesamte Tabelle … und sperrt sich damit für ein on Update Cascade selbst aus 😐
Also doch die etwas härtere Tour:

  1. interne Schlüsselbeschränkung aufheben
  2. Immer gleichzeitig die ID und den Vater-Verweis um die gefundene Zahl dekrementieren (da von allen Werten ein konstanter Wert abgezogen wird entstehen wiederum gültige und konsistente Datensätze
  3. Vaterbeziehung wieder mit einem Schlüssel versehen

Warum so umständlich? – Auto-Increment-Rücksetzen funktioniert mit InnoDB als Engine nicht, siehe auch weiter unten. Fremdschlüssel generell abschalten wäre auch keine Maßnahme gewesen, denn die externen Referenzen haben ja ein „on update cascade“ – das erspart die langwierige Nacharbeit an x anderen Tabellen in der Datenbank.
Somit kann jetzt wenigstens erst mal wieder gearbeitet werden – die Arbeit für den Entwickler fängt aber jetzt erst richtig an…
Eine andere Alternative wäre die Vergrößerung des Schlüsselbereichs z.B. auf Bigint gewesen oder evtl. auch nur die Verwendung nicht-negativer Zahlen. Leider nicht ganz so einfach möglich, da die Tabelle an verschiedenen Stellen in der Datenbank referenziert wird und natürlich auch die referenzierenden Schlüsselwerte im Datentyp identisch sein müssen. Das wäre auf die Schnelle auch nicht so trivial gewesen.

Zeit für etwas Ursachenforschung – wie man so schön sagt: Lessons learned. Wie Allgemein üblich handelt es sich bei diesem Fehler nicht um eine singuläre Fehlerquelle sondern das Zusammenspiel mehrere Umstände.
Erste Fragestellung: Warum waren die ID-Werte überhaupt so ins Kraut geschossen?
Hierfür gibt es mehrere Gründe – einer ist die historische Wurzel der Datensätze: Diese stammten aus einer Access-Tabelle mit Replikaten. Hierbei setzt Access den Primärschlüssel nicht mehr auf auto_increment (monoton ansteigend wie sich das gehört) sondern auf Random – und da kommen dann recht fix große und auch negative Zahlen bei raus. Die hatte ich natürlich einfach übernommen.
Nächstest Problem: In der Tabelle fanden in letzter Zeit größere Mengen an Transaktionen statt: Die Tabelle wird teilweise aus einer anderen Tabelle gespeist, dabei werden die Datensätze automatisch generiert. Dieses Tool musste aufgrund von Fehlern mehrfach laufen und hatte teilweise dabei auch größere Mengen Einträge produziert – rund 600.000 an der Zahl bevor der Fehler auffiel und beseitigt werden konnte. Somit war man schon bedrohlich nahe ans Limit heran gekommen.
Soweit zur suboptimalen Verwendung der vorhandenen Schlüsselzahl – aber das war ja nicht alleine ausschlaggebend, immerhin könnte man ja erwarten, dass ein ausgewachsenes Datenbank-System sich selbständig um die Behebung des Notstandes: „Keine Schlüssel mehr da“ kümmert. Zumindest sollte man das annehmen. Nun kommen auch hier einige kleinere Probleme zusammen, die dann gemeinsam für den großen Ärger sorgen. Als Grundlage verwendet MySQL zwei verschiedene Datenbank-Engines – MyIsam als klassische Tabellenform, leider kann diese nicht mit Fremdschlüsseln umgehen. Daher gibt es InnoDB als neueren Standard. Diese hat zwar nicht die Performance in Sachen Volltext-Suche, kann dafür aber von Hause aus bereits mit Fremdschlüsseln umgehen. In der aktuellen Entwicklung ein unumgängliches Feature, das kaum ein Datenbankentwickler heute noch missen möchte.
Der Berechnungsalgorithmus für die nächste ID in InnoDB ist recht simpel gestrickt: Die nächste zu verwendende ID berechnet sich stets als max(id)+1 – was in MySQL bei Integer-Werten mit 32 Bit zu einem Fehler führt (erst ab Version 5.5) – der dann durch die Ebenen der Datenbank-Engine nach oben wandert und dort schließlich zum bekannten Fehler führt – leider ist der Hinweis duplicate key for primary da nicht sonderlich hilfreich um gleich die Ursache zu erkennen. Dieser Algorithmus wird auch angestoßen, wenn man den Auto-Increment-Zähler manuell versucht zurück zu setzen, oder bei einem Server-Neustart. Simple Lösungen mag ich ja eigentlich, aber so simpel hätte es in dem Fall doch nicht sein müssen.
Interessanterweise ist der Fehlerfall ja sogar in der Dokumentation beschrieben (siehe hier:) – netter Nebeneffekt: Wenn man als Datentyp BigInt (also 64 Bit-Zahlen) verwendet, dann klappt es auch mit dem Wrap-Around am Ende des Zahlenbereichs, es wird dann wieder bei 1 bzw. im negativen Bereich weiter gezählt. Warum habe ich noch nicht ergründen können, ich vermute mal, dass InnoDB intern schon mit 64 Bit arbeitet.

Kommen wir zu den Lehren für weitere Projkete oder auch nur die Neuanlage neuer Tabellen:

  1. An InnoDB führt kein Weg vorbei wenn Fremmdschlüssel im Spiel sind, und Fremdschlüsselmechanismen sind generell begrüßenswert
  2. Beim Import von Alt-Daten wenn es möglich ist, deren ID-Werte verwerfen und neue vergeben (leider nicht immer ohne größeren Aufwand möglich)
  3. Wenn sinnvoll möglich BigInt als Datentyp für die ID-Spalte verwenden, das schafft Luft und es klappt auch mit dem Überlauf richtig
  4. Für den Fall der Fälle Mittel in Bereitschaft haben, um das Problem durch „Renumerierung“ lösen zu können (nicht immer hat man die Zahlen in einem bestimmten Bereich liegen so wie in diesem glücklichen Fall).

Weitere Kommentare herzlich willkommen.

Die unbeschreibliche Macht des Doppelklick

Da bekomme ich mal wieder eine Nachricht: Da funktioniert was nicht richtig, kannst du mal helfen …
Klar kann ich das in aller Regel – ist ja mein Job, und in dem Fall auch noch meine Software.

Wie in so vielen Fällen (ja auch meine Software hat mal den ein oder anderen Fehler, ich bin auch nur ein Mensch), liegt das Problem aber nicht bei meiner Programmierung sondern sitzt ca. 80cm vor dem Bildschirm. Genauer gesagt liegt das Problem rund 60cm neben selbigem. Die Rede ist von nichts anderem als einem Zeigerät – wahlweise als Maus, Nager, Ratte, Trackball oder Touchpad zu bezeichnen.

Die Verwendung ist denkbar einfach: Aufgrund von Bewegungen des Geräts (oder entsprechenden Sensoren bei Touchpads und Trackballs) kann man den Mauszeiger über den Bildschirm bewegen. Für die Leute die es sich nicht vorstellen können: Es gab auch eine Zeit, da bediente man Rechner nicht über eine graphische Oberfläche oder musste diese gar von Hand erst starten. Auch heute ist solches Wissen oftmals Gold wert. Wer regelmäßig im Remote-Bereich arbeitet weiß wie mächtig die angeblich so dröge Kommandozeile sein kann (egal für welches Betriebssystem) – nur erfordert sie deutlich mehr Wissen und Mitdenken des Benutzers. Das will man nicht immer, gerade wenn man für andere Arbeiten den Kopf frei haben möchte.

Aber zurück zum eigentliche Problem, bzw. dessen genauerer Eingrenzung: Während die Interaktion des Mauszeigers mit der Bewegung der Hand für die allermeisten Benutzer recht zügig zu erlernen und intuitiv verstanden wird (wenn nicht gerade jemand die Auswertung von X und Y-Koordinate vertauscht hat – ein großer Spaß um Kollegen zu verwirren), ist die nächste Ebene der Interaktion schon deutlich komplexer: Es geht um die Tasten die auf dem Gerät vorhanden sind. Die aktuellen Ausführungen bestehend zumeist aus drei Tasten:
[list]
[*]links
[*]rechts
[*]Mitte (oftmals als Rad ausgeführt)
[/list]
Das Mausrad als eine eigentlich recht späte Entwicklungsphase des PC-Nagers (eingeführt von Microsoft mit der IntelliMouse im Jahr 1996), häufig ist er mit der praktischen Funktion verknüpft durch den aktuellen Bildinhalt zu scrollen (je nach Ausführung sogar waagrecht, in aller Regel jedoch nur vertikal) – diese Funktionalität hatte man früher auf der Tastatur mit den Pfeiltasten – auch heute funktioniert das noch erstaunlich gut. Eigentlich eine sehr komplexe Funktion, dennoch wird sie intuitiv meist richtig angewandt.

Die eigentlichen Funktionstasten der Maus bereiten hingegen immer wieder Probleme, obwohl die Funktion denkbar einfach ist: Durch das Drücken einer Taste wird ein Schalter geschlossen – der Rechner erkennt welche Taste gedrückt wurde und meldet das der Software weiter, damit diese darauf reagieren kann. Wie die Reaktion ausfällt ist von Software zu Software unterschiedlich. Software meint in diesem Zusammenhang auch ausdrücklich das Betriebssystem des Rechners (also Windows, Linux, MacOS – ob man es glaubt oder nicht, auch das ist „nur“ Software).

Im allgemeinen hat sich ein gewisser Standard herausgebildet – die Maustaste unter dem Ringfinger (bei 3 Tasten) bzw. dem Mittelfinger (bei 2 Tasten) öffnet ein Menü mit zusätzlichen Funktionen, abhängig vom Kontext. Daher auch der Name Kontext-Menü. Schon die Verwendung dieser alternativen Funktion ist vielen Benutzern nicht wirklich geläufig. Dabei ermöglichen sie dem Programmierer von graphischen Oberflächen eine direkte Möglichkeit auf die Bedürfnisse des Benutzer so spezifisch als möglich zu reagieren. Denn vielfach kann man heute nicht mehr zwingend vorhersagen was sich genau an welcher Stelle auf dem Bildschirm finden wird, auch das ein Folge von graphischen, fenster-basierten Benutzeroberflächen.

Noch weniger bekannt (und oftmals auch ohne weitere Funktion) ist die Tastenfunktion des Mausrades – diese variiert von Software zu Software stark.

Bleibt eigentlich nur noch eine Taste übrig, im Regelfall als linke Maustaste bezeichnet, allgemeiner ist es die unter dem Zeigefinger. Mit dieser Taste führen wir die allermeisten Operationen aus – von Markieren durch Ziehen, Positionieren des Cursors im Programmcode bis hin zum Anwählen/Anklicken von Schaltflächen verschiedenster Art. Eigentlich ganz logisch, der Mauszeiger als verlängerter, digitaler, virtueller Zeigefinger. Leider hat sich unter Windows die Technik des Doppelklicks zum Starten von Programmen etabliert (unter Linux reicht ein einfacher Klick) – mit teilweise skurielen Auswüchsen: Auf nahezu alles und jedes wird doppelt drauf geklickt – ob sinnvoll oder nicht! Da wird im Netz auf Links ein Doppelklick ausgeführt, weil man ja eine Aktion erwartet und man bei Rechnern immer alles mit Nachdruck tun muss … so oder ähnlich muss sich das Doppelklicken bei einigen Leuten ins Gedächtnis eingebrannt haben. Ich frage mich manchmal was wohl passiert wäre, wenn Microsoft damals es für praktischer befunden hätte den Tripleclick oder gar den Penta-Klick als Startoperation zu verwenden …

Falls irgendjemand eine Idee hat, wie man die „Seuche“ der Doppelklickeritis auszurotten – ich bin für jede Anregung dankbar. Ich habe ja schon überlegt, ob ich einfach überall ein JavaScript einbaue, das zentral die Doppelklicks zählt und dann ggf. nach übermäßiger Menge eine Fehlermeldung ausgibt. Ob das wirklich zielführend ist, wage ich zu bezweifeln. In diesem Sinne „happy x-fach clicking“

RPM und andere Höllen

Da denkt man an nichts böses und will nur mal eben eine aktualisierte Software auf dem Server einspielen. Eigentlich keine größere Sache sollte man meinen, sind doch die aktuellen Linux-Distributionen allesamt mit einem Paket-Management-System ausgestattet, das es erlaubt Software recht einfach zu installieren und zu warten. Wenn alles glattläuft, wie gesagt, wenn …

Angefangen hat alles mit einer Fehlermeldung, dass in einem Webinterface die Bilder nicht angezeigt werden. Im Hintegrund werkelt ein PHP-Skript, das die Bilder bei Bedarf auf die passende Größe runter sakliert – das senkt den Bandbreitenbedarf und die Übetragungszeiten für eine Website. Zudem hat man dann als Autor noch ein wenig mehr Einfluss auf die Qualität – die Skalierungsmechanismen der verschiedenen Browser sind nicht alle wirklich gut.

Ursprünglich habe ich mal auf die integrierte GD-Bibliothek von PHP gesetzt allerdings musste ich recht bald schon feststellen: Die ist für einfache Dinge ganz brauchbar (und in den meisten Fällen auch einfach schon vorhanden), aber die Performance ist nicht unbedingt berauschend. Gerade aber für die Erstellung von Bildergalieren on-the-fly ist die Performance (oder die Ungeduld des Benutzers) wichtig. Fündig geworden bin ich bei der Imagick-Erweiterung für PHP. Diese basiert auf der ImageMagick-Bibliothek. Diese hat den Vorteil direkt in C geschrieben zu sein und die Performance macht echt Freude. Zudem sind die Möglichkeiten doch mittlerweile sehr ausgefeilt.

Wie nahezu jede Software hat sich auch Imagick weiterentwickelt und damit gab es einige Veränderungen an den Schnittstellen und Funktionen – daher funktioniert die Bilder-Skalierung nicht mehr wie gewünscht: Auf dem Testserver hatte ich eine aktuellere Version installiert und auch auf dieser Basis programmiert – mit der älteren wollte das nicht so recht harmonieren, es fehlten einfach ein paar Funktionen bzw. diese wurden verändert und teilweise umbenannt.

Nun gut, dann aktualisiert man doch einfach auch mal das produktive System. Da mein Hosting-Anbieter zum Installationszeitpunkt nur die Wahl zwischen Plesk (alles vorgefertigt und Clicki-Bunti) oder CentOS 5 minimal geboten hatte, habe ich mich notgedrungen für CentOS als RedHat-Devirat entschieden, denn auf einigen Komponenten möchte ich doch gerne etwas mehr Einfluss nehmen als es Plesk von Hause aus zulässt (Stichwort Mailserver, Virenscanner und Authentifizierungsmethoden). CentOS ist ein Unternehmenslinux und das merkt man – hier wird viel Wert auf stabile und sichere Software gelegt, weniger darauf die aktuellsten Funktionen bereit zu halten. Als Folge habe ich damals schon einige Software von Hand kompiliert und installiert – eine Zeit lang hatte ich ja auch mit Linux from Scratch gearbeitet – also kein wirkliches Neuland für mich. Allerdings klappt es nicht immer auf Anhieb, die Software selbst zu installieren und nur bestimmte Teile direkt wie geliefert zu verwenden. Aber es hat ja am Ende alles funktioniert. Nur mit den Updates muss man dann halt vorsichtig sein.

Das letzte Update ist denn auch irgendwo mal stecken geblieben. Als Folge davon gab es noch nicht vollendete Transaktionen des Paket-Managers (yum) – mit

yum-complete-transaction

lässt sich soetwas auch beheben. Nur ärgerlich wenn das auch nicht klappt, weil dem virtuellen Server mal wieder der Arbeitsspeicher ausgeht – also erst mal einige Server-Prozesse stoppen, und dann das Update machen … Immerhin läuft es dann auch durch. Auch wenn noch ein paar Klippen wie beschädigte Repository-Datenbanken zu beheben sind.

Wie sich herausstellt: Leider ist mit den verwendeten Quellen / Repositories keine aktuellere Version von Imagick zu bekommen. Aber es gibt ja alternative Quellen wie das Remi-Repository – dort finden sich viele Pakete aktuellerer Software als in den offiziellen Quellen von CentOS. Das führt ggf. zu doppelten und widersprüchlichen Paketen und Abhängigkeiten die sich im Kreis drehen. Mit ein wenig Nacharbeit kann man diese Abhängigkeiten aber auflösen, indem man explizit vorgibt, welche Version aus welcher Quelle man denn nun haben möchte. Damit lässt sich schon mal die aktuelle Fassung von ImageMagick installieren (das Paket dazu im Remi-Repository heißt ImageMagick2).

Wie ich bereits geschrieben hatte, habe ich einiges an Software selbst kompiliert – unter anderem PHP, da es das von Haus aus bei CentOS nicht mit den Erweiterungen und Einstellungen und vor allem in einer aktuellen Fassung gab. Das macht mir natürlich jetzt etwas Probleme, denn die Imagick-Erweiterung lässt sich nicht so ohne weiteres per Paket-manager installieren. PHP hat dafür mittlerweile etwas vergleichbares wie einen Paket-Manager – nur auf Quellcode-Basis mit automatischer Kompilierung. Vergleichbar ist das mit dem CPAN-Archiv von Perl – auch dort werden viele Module für Perl bereit gehalten und lassen sich damit auch problemlos installieren und auf dem aktuellen Stand halten. Bei PHP heißt das Tool dazu PECL (PHP Extension Community Library), zudem gibt es noch PEAR (PHP Extension and Application Repository) – beide arbeiten recht gut zusammen und machen das Leben mit PHP um einiges leichter.

PECL ist das richtige Stichwort – es bietet unter anderem auch die Option die Imagick-Erweiterung zu installieren. Leider ist die Freude erst mal nur kurz – das verwendete PHP-Modul wird weiterhin gegen die veraltete Version (6.2.x) von Imagick gelinkt. Klar die hatte ich ja nicht deinstalliert, denn das Package ImageMagick2 darf parallel zu ImageMagick installiert sein. Also das alte endlich mal loswerden und per Paket-Manager deinstallieren. Aber oh Wunder – PECL kann auf einmal das Modul nicht mehr bauen….
Lösung: Es fehlten die notwendigen Header-Dateien zum Paket ImageMagick2 – das Paket dazu heißt praktischerweise ImageMagick2-devel (für development-version) – das bringt die Header mit, im normalen Betrieb werden die nicht benötigt, nur wenn man eben selbst Software unter Verwendung der Library schreiben oder kompilieren möchte.
Damit klappt dann auch endlich die Kompilation des aktuellen Modules. Nochmal den Webserver neu starten und dann funktioniert auch endlich die ursprünglich gewünschte Anzeige der Bilder. 🙂
Mittlerweile ist es mal wieder kurz vor 0:00h geworden, aber die Mühe und der Bastelaufwand waren ja vom Erfolg gekrönt.

Nur noch kurz testen ob auch sonst alles auf dem Server noch richtig läuft – beim Update wurden ja doch auch einige andere Pakete gleich mit aktualisiert. Und – oh Schreck! – der Mailserver mag nicht … genauer gesagt, das Filter-Daemon amavisd kommt nicht mehr richtig in Gang. Amavisd ist die zentrale Schnittstelle zwischen Mailserver und diversen Viren und Spam-Scannern. Ohne die Software, kein e-mail-Vekehr.
Der Neustart von Amavisd bringt die Ursache recht schnell an den Tag: Da sind einige Perl-Module beim Update zu Bruch gegangen, wahrscheinlich, weil sie in verschiedenen Versionen installiert wurden – einmal per CPAN und einmal per Paket-Manager der Distribution (leider sind sich diese beiden Werkzeuge in den seltensten Fällen wirklich grün). Das manuelle Entfernen der Module aus der Installation bringt auch das wieder ins Lot.

Nun stellt sich nur noch der Virenscanner Clam-AV etwas bockig – das Scanner-Daemon will und will nicht starten – Ursache ist diesmal aber nicht die Software sondern leider mal wieder die Beschränkungen der Virtualisierung – zum Starten braucht Clamd recht viel Arbeitsspeicher: ca. 100 Megabyte – bei nur 768 MB virtuellem RAM kann es aufgrund der anderen Prozesse wie MySQL, Apache, Postfix usw. da recht bald recht eng im Speicher werden. Da MySQL sich doch auch recht gut im Speicher breit macht (klar Cache braucht nunmal Platz) ziehe ich dort die Daumenschrauben ein wenig fester an, dann reicht es auch noch für den Start von Clamd – aber gut ist diese Lösung auf Dauer nicht, ich werde wohl oder übel einen anderen Server brauchen, ob es wieder ein virtualisierter wird, weiß ich noch nicht so genau. Auf alle Fälle machen derartige Nachschichten nicht wirklich Spaß

Ein echter Mangel – mehrere Dimensionen in HTML

Die Erde ist eine Scheibe … dieser fehlerhaften Auffassung ist ein Großteil der Menschheit mittlerweile entwachsen. Leider ist die mehrdimensionale Denkweise in Rechnern immer noch ein Problem. Vor allem in einem der modernsten Kommunikationsmittel überhaupt. Das WWW ist bestenfalls noch zweidimensional, aber eigentlich ja noch nicht mal das.

Jetzt werden einige einwenden, dass ein Bildschirm doch schon immerhin mal 2 Dimensionen (Breite und Höhe) mitbringt, und es auch schon Ansätze für dreidimensionale Videos im Netz gibt – alles recht und schön – nur das Datenformat HTML ist leider so flach wie man es sich nur irgend vorstellen kann. Es sollte halt möglichst universell und einfach sein – so war einmal die Philosophie.

Schlecht gefahren ist man damit nicht – vor allem in der Anfangszeit des Netzes war es auch mehr als ausreichend – lästig wird die flache Struktur, wenn man nicht mehr nur die Anzeige einer Seite betrachtet sondern an Web-Applikationen denkt.

Altbekannt ist den meisten Leuten das Dilemma mit den Tabellen – jeweils eine Zeile und dann die Spalten Stück für Stück füllen. Soweit ja kein Problem und mit vergleichsweise wenig Aufwand lässt sich auch aus jeder mehrdimensionalen Struktur (z.B. einer Datenbank) eine „plattgeklopfte“ Variante erzeugen die für die Anzeige taugt.

Umgekehrt wird es aber ungemütlich – ich habe es beruflich immer wieder mit tabellenförmigen Daten zu tun, die auch als solche am leichtesten intuitiv eingegeben werden können. Klassisches Beispiel ist die Relation von Mitarbeitern und deren geleisteten Arbeitsstunden – im allgegenwärtigen Excel würde man kurzerhand hergehen und je nach Gusto in die Zeilen die Personen und in die Spalten die Tage des zu betrachtenden Zeitraums eintragen, wahlweise geht es natürlich auch mit vertauschten Rollen zwischen Zeilen und Spalten. Aber: Jede Zelle in Excel lässt sich wunderbar über Zeile und Spalte adressieren. In HTML gibt es für entsprechende Formularlemente nur einen Referenz-Wert: Das name-Tag – denn nur dieses wird als Schlüssel für die serverseitige Auswertung geliefert, immer ein Satz Tupel. Zwar gibt es gerade in PHP Vereinfachungen auf Basis der Benennung um beispielsweise multiple-Choice etwas leichter zu machen, aber das hilft auch nur bei Ankreuz-Antworten.

Was bleibt ist eine eigene Logik zu erstellen nach der die Felder benannt werden und dann anhand dieser die Felder eindeutig zu kennzeichen – z.B. mittel AxB wobei A und B beliebige ganze Zahlen sind. Das macht auf der Serverseite oder auch im JavaScript natürlich wieder jede Menge Aufwand. Ein anderer Weg ist mir derzeit aber nicht bekannt. Ich frage mich, mit welcher Version von HTTP und/oder HTML endlich vernünftige Möglichkeiten geschaffen werden mehrdimensionale Eingaben standardisiert zu behandeln.

Wie gesagt: So selten sind mehrdimensionale Daten in der realen Welt gar nicht – sie ist ja bekanntlich auch keine Scheibe.

PHP-Freuden: json_encode und Zeichenkodierungen

Mal wieder etwas aus der PC-Ecke bzw. für die Fehlersuche in der Programmierung in PHP und JavaScript.

[b]Hintergrund: [/b]
Es gibt eine Benutzermaske als Webinterface, eine Art Warenkorb. Aus einer Auswahlliste können Elemente für den Warenkorb ausgewählt werden. Per AJAX wird dann die Session auf dem Server aktualisiert und der Warenkorb gespeichert. Soweit nichts Ungewöhnliches. Die notwendigen Beschreibungen mit allen Details werden als Rückgabe-Wert in Form eines HTML-Schnippsels geliefert, das erspart einige Verrenkungen in JavaScript.

[b]Fehlerbeschreibung: [/b]
Das Hinzufügen klappt nicht zuverlässig – genauer gesagt: Es wird ordnungsgemäß etwas hinzugefügt, aber es wird nicht richtig angezeigt.

[b]Analyse:[/b]
Die Datenbankabfragen und das Zusammenstellen der Informationen verläuft in PHP alles richtig. Einzig die Funktion „json_encode()“ will nicht so ganz wie sie soll – anstelle den Schnippsel und andere Zusatzinfos (z.B. die geschätzte Verfügbarkeit/Lieferzeit nach aktuellem Stand) in ein Objekt zu packen, steht dort nur „null“ – also rein gar nichts.

[b]Erklärung/Lösung:[/b]
An wenig prominenter Stelle wird darauf hingewiesen, dass die Funktion zwingen mit UTF-8-konformen Werten gefüttert werden muss. Leider übergeht der Parser ungeeignete Werte einfach und ersetzt sie durch „null“ ohne das dabei der Fehlercode gesetzt wird, daher liefert ein Aufruf der Funktion „json_last_error()“ auch keinerlei Info darüber was schief gegangen ist. Lösung ist recht simpel: „utf8_encode()“ über den Schnippsel jagen und schon funktioniert alles wie erwartet. 😀

Aber bis man auf die Idee überhaupt kommt vergeht ganz ordentlich Zeit – zumal leider fast jede Antwort des Servers leider Beschreibungen mit Umlauten liefert.