Alte Homepage wieder verfügbar
30 Apr
Meine alte Homepage (2000-2005) ist wiederauferstanden und unter http://old.manuel.kiessling.net/ erreichbar.
30 Apr
Meine alte Homepage (2000-2005) ist wiederauferstanden und unter http://old.manuel.kiessling.net/ erreichbar.
8 Apr
Ein Kollege von mir, Max Winde, hat in den vergangenen Wochen ein Tool geschrieben welches sich innerhalb kürzester Zeit zu einem Renner in den verschiedensten Abteilungen entwickelt hat, und schon jetzt aus dem Arbeitsalltag kaum noch wegzudenken ist: siqqel.
Die Mächtigkeit von siqqel liegt darin, dass es den Applikationsstack, der benötigt wird, um Anfragen an die Datenbank zu übermitteln, die Antwort entgegenzunehmen und die empfangenen Daten darzustellen, auf etwas recht bekanntes und verbreitetes beschränkt: den Browser.
SQL Queries werden direkt in einer statischen HTML Datei notiert. Per Ajax werden diese an ein zentral abgelegtes Backend-Skript übermittelt. Das Result Set wird an den Browser zurückgeliefert und direkt dort per DHTML dargestellt. Mit (D)HTML Bordmitteln, JavaScript und CSS kann man direkt innerhalb des HTML Dokuments dann beliebig flexibel mit den Result Sets arbeiten.Richtig, ganz ohne PHP geht es nicht. Es braucht einen Punkt im Backend, welcher den SQL Query vom Browser entgegennimmt, an die DB übermittelt, und das Result Set als JSON an den Browser zurückliefert. Aber man beachte die Vorteile zur vorhin beschriebenen Insellösung:
Nun muss ein Systemadministrator die PHP Backendskripte unter http://intranet/secure/siqqel/ hinterlegen, und die Konfiguration anpassen um dem siqqel PHP Code Zugriff auf die genannte Datenbank zu ermöglichen.
Ein siqqel User muss dann nur eine HTML Datei erzeugen (auf seinem Desktop oder wo auch immer, ein LAMP Kontext wird ja nicht benötigt), die folgendes enthält:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://intranet/secure/siqqel/siqqel.js.php"> </script>
</head>
<body>
<table sql="SELECT * FROM data.product"></table>
</body>
Öffnet er diese Datei lokal in seinem Browser, wird das SQL Statement im Attribut der Table an das Backend Skript übermittelt, das Result Set als JSON zurückgegeben, und der Inhalt der Datenbanktabelle automatisch in das table Element gerendert.
Von hier aus hat man alle Möglichkeiten: Man möchte mehrere Tabellen auf einmal anzeigen? Man erzeugt einfach mehrere table Elemente mit den entsprechenden Queries. Man möchte alle Zeilen im Result Set, bei denen die Spalte name mit “a” beginnt in der HTML Tabelle hervorheben? Kein Problem, jede Tabelle, Zeile und Spalte liefert ein “loaded” Event, also hat man mit einem JavaScript-Konstrukt wie
$('td.name').live('loaded', function(name) {
// do something useful.
});
alle Möglichkeiten. Der Client Teil von siqqel basiert auf jQuery, also kann man schnell und einfach Reports bauen mit allen sinnvollen und sinnlosen Möglichkeiten, die jQuery bietet.
Was sind die weiteren Vorteile? Nun, die HTML Datei ist nicht nur der View des Reports, die HTML Datei IST der Report. Man kann ihn in die vielleicht vorhandenen Coderepositories im Unternehmen packen, man kann ihn per Mail verschicken, man, wenn die Wikisoftware es zulässt, seine Reports sogar direkt nativ in eine Wikiseite packen und so besonders effizient mit den Kollegen im Unternehmen teilen.
Die Projektseite von siqqel ist http://github.com/MyHammerOpenSource/siqqel. Nicht wundern, bis vor kurzem hieß das Projekt noch “sqlHammer”, der Begriff mag noch an verschiedenen Stellen auftauchen.
Bei Fragen zu siqqel empfehle ich, ein Issue Ticket bei github zu öffnen, oder wendet euch an opensource@myhammer.com.
26 Feb
Dieser Artikel ist Work in Progress!
Dieses Dokument beschreibt Werkzeuge und Prozesse, um Datenbankänderungen innerhalb von großen Softwareprojekten einfach, fehlerfrei und nachvollziehbar durchzuführen und zu managen.
Zentraler Ansatz dieser Lösung ist: Datenbankänderungen und Codeänderungen sind prinzipiell genau dasselbe. Denn Datenbankänderungen haben genau wie Codeänderung die folgenden Eigenschaften:
Wenn wir Datenbankänderungen in diesem Sinne genau wie Codeänderungen verstehen, macht es auch Sinn, Datenbankänderungen genau wie Codeänderungen zu behandeln. Und das bedeutet, diese innerhalb des bereits vorhandenen Entwicklungsprozesses zu managen und im selben VCS Repository zu verwalten.
Unter Datenbankänderungen müssen wir verstehen: Alle SQL Statements, welche die Strukturen oder Inhalte einer Datenbank verändern.
Eine Datenbankänderung im Zuge eines Projekts, Bugfixes oder sonstigen Tickets ist daher folgerichtig eine Sammlung von SQL Statements, welche zusammen mit den Codeänderungen des zugehörigen Tickets im selben Branch vom Entwickler hinterlegt wird. Hinzu kommt, dass es eine klar definierte Lokalität für diese Änderung geben muss, damit ein Raum geschaffen ist, in dem Konflikte entstehen (und gelöst werden) können. So wie die gleichzeitige Änderung an der Datei myFile.txt in zwei verschiedenen, zu mergenden Branches zu einem Konflikt führt – da in beiden Branches die Datei den selben Speicherort, also dieselbe Lokalität besitzt – müssen auch Änderungen an derselben Tabelle in zwei Branches innerhalb derselben Lokalität des jeweiligen Branches stattfinden. Der vorgeschlagene Ansatz ist daher, die Struktur der Datenbank, also die Databases mit den darunterliegenden Tables, in einer analog aufgebauten Ordner-Datei-Struktur abzubilden.
Die Lokalität für die Tabelle users.hobbies wäre beispielsweise die Datei /dbchanges/users/hobbies.sql innerhalb des VCS. Abgebildet wird die gesamte DB Struktur, also alle Databases mit allen ihren Tables:
/dbchanges/users/hobbies.sql
/dbchanges/users/contact.sql
...
/dbchanges/products/colors.sql
/dbchanges/products/forms.sql
...
und so weiter. Gerade bei komplexen Datenbanken macht es natürlich Sinn, diese Struktur mit einem Skript zu erzeugen, für MySQL kann man dazu in einem beliebigen Verzeichnis auf dem DB Server folgenden Code ausführen (geht davon aus, dass die MySQL Daten unterhalb /var/lib/mysql liegen):
find /var/lib/mysql -type f -name *.frm -exec dirname {} \;| cut -d "/" -f 5| xargs mkdir -pfind /var/lib/mysql -type f -name *.frm | cut -d "/" -f 5,6 | sed "s/.frm/.sql/g" | xargs touch
Diese Dateien nenne ich im folgenden DB Change Container.
Wichtig ist, dass sämtliche DB Change Container nach einem Release, nachdem diese Änderungen also auf dem Produktivsystem angewendet wurden, wieder leer sind – denn zum Start der Produktion eines neuen Releases liegen noch keine neuen Änderungen für die DB vor.
Nun beginnen die Entwickler, Tickets (Feature Requests, Bugs etc.) umzusetzen, einige gemeinsam in einem Branch, einige in eigenen Branches. Sind im Zuge einer Implementation Datenbankänderungen notwendig, hinterlegt der Entwickler innerhalb des zugehörigen Branches diese Änderungen nach folgendem Muster:
Der Entwickler legt alle benötigten Statements in der Datei /dbchanges/users/hobbies.sql ab:
USE users;
ALTER TABLE hobbies ADD newfield1 INT NOT NULL AFTER userId;
ALTER TABLE hobbies DROP oldfield;
ALTER TABLE hobbies ADD newfield2 TINYINT NOT NULL;
ALTER TABLE hobbies ADD INDEX (newfield2);
INSERT INTO hobbies ( id, name, value ) VALUES (1234, 'hobbyname', 'hobbyvalue');
Er erzeugt dazu eine neue Datei /dbchanges/users/pets.sql und füllt sie mit dem CREATE Statement (sowie ggf. INSERT Statements):
USE users;
CREATE TABLE pets(
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
petname VARCHAR( 64 ) NOT NULL,
FULLTEXT ( petname )
);
Er erzeugt einen neuen Ordner /dbchanges/products und darin eine Datei colors.sql mit folgendem Inhalt:
CREATE DATABASE products;
USE products;
CREATE TABLE colors (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
colorname VARCHAR( 24 ) NOT NULL);
Er füllt die Datei /dbchanges/products/colors.sql mit folgendem Inhalt:
USE products;
DROP TABLE colors;
Ansonsten läuft der Entwicklungsprozess wie gewohnt.
Werden nun verschiedene Tickets für den Release gebündelt, werden die einzelnen Branches wie gehabt gemerged. In Hinblick auf die DB Änderungen passiert nun folgendes:
Sämtliche Änderungen in den einzelnen Branches unterhalb von /dbchanges werden naturgemäß unterhalb /dbchanges im Merge zusammengeführt. Hierbei greifen die bekannten VCS Mechanismen: Wurden Änderungen in einer Datei nur in einem einzigen Branch oder Commit vorgenommen, werden diese Änderungen einfach angewendet. Wurden Änderungen an einer Datei (also innerhalb derselben Lokalität) in mehreren Branches vorgenommen, kommt es zu einem Konflikt.
Dies ist der erste wichtige Mechanismus der hilft, die drei Anforderungen – einfach, fehlerfrei und nachvollziehbar – zu gewährleisten: Da der Konflikt garantiert eintritt, ist auch garantiert, dass der Vorgang völlig automatisch die notwendige Aufmerksamkeit erzeugt und nicht übersehen werden kann.
Nun muss, wie auch bei Codekonflikten, gelöst werden: Machen beide Änderungen Sinn, oder widersprechen sie sich? Wie genau kann man sie am sinnvollsten zusammenführen? Relevant ist hier nur, dass am Ende ein Set an Änderungsanweisungen in den Approval committet wird, welches in sich rund ist. Falls es eine eigene Test oder QA Datenbank gibt auf die diese Änderungen angewendet werden müssen, wird dies gemacht nachdem alle Tickets fertig gemerged wurden.
Wurde im Vorfeld alles richtig gemacht, muss im Zuge des Rollout oder Release nur noch das zusammengefasste Set an Änderungen ermittelt werden, und diese müssen dann, entsprechend ihrer jeweiligen Eigenschaft, ausgeführt werden. Die Summe der Änderungen ergibt sich aus der Summe aller Anweisungen in den DB Change Containern unterhalb /dbchanges – hier macht es natürlich Sinn, dass man diese mithilfe eines Skripts “zusammensammelt”, aber ich gehe hier nicht näher darauf ein.
Nach dem Rollout/Release, und vor dem Erzeugen neuer Branches, müssen dann im Trunk sämtliche Datenbank-Änderungsanweisungen aus den DB Change Containern entfernt werden (auch hier macht ein Skript wie z.B.
for f in `find . -type f -name *.sql`; do echo -n "" > $f; done
Sinn, um diesen Schritt zu vereinfachen), und dies muss in den Trunk (oder von wo aus auch immer neue Branches gebildet werden) committet werden – denn sonst würden dieselben Änderungen beim nächsten Rollout erneut angewendet werden.