Too Cool for Internet Explorer

Apple discontinues the xserve


This is very, very disappointing: Apple discontinues the xserve. It will be available for order until 31th of january 2011. The recommendation of Apple is to buy the Mac mini Server edition or the Mac Pro in future. While the Mac Pro is a great product, i can't imagine putting it in a 19" rack: two Mac Pro on a shelf would take the place of 12 units -- wow.

At ClipDealer we have two of the xserve. They are great machines and i will definitly miss the possibility to buy more of them.

R.I.P. xserve -- i had a very good time with you.



nginx tips: adding arbitrary output to a response


A while ago i wrote about how to use nginx as a proxy to do cookie based redirects. We use this functionality at work to provide easy access and view the progress of development of each developer.

I thought it would be nice to have the information, of which developers machine you currently have access to, right on the website. But i always disliked to put functionality to accomplish this inside the framework or even the app itself or have to install / modify some special configuration on each developers machine. I always wanted my proxy to do this kind of work. ... And nginx can.

It's the substitution module of nginx, that can replace arbitrary text in a http response. Nginx must be compiled with the option --with-http_sub_module configured.

The following rows show how to fill a variable $name with the name of the developer we are accessing the machine of. The statement sub_filter defines the search pattern as first parameter and the replace string as second parameter -- very easy, isn't it?

set $name ""
if ($http_cookie ~* "(; )?devredirect=([^;]+)") {
    set $name $2;
}
sub_filter      "</body>"       "<div style='position: fixed; 
    left: 0; top: 0; font-weight: bold; padding: 5px; color: #000; 
    background-color: rgb(235,58,0);'>devredirect: ${name}</div>
    </body>";

The following rows show the part of the proxy configuration with the inserted substitution filter:

server {
    listen          80;
    server_name     .clipdealer.devcenter.int;

    proxy_redirect          off;
    proxy_set_header        Host            $host;
    proxy_set_header        X-Real-IP       $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;

    location / {
        set $name ""
        if ($http_cookie ~* "(; )?devredirect=([^;]+)") {
            set $name $2;
        }
        sub_filter      "</body>"       "<div style='position: fixed; 
            left: 0; top: 0; font-weight: bold; padding: 5px; 
            color: #000; background-color: rgb(235,58,0);'>
            devredirect: ${name}</div></body>";

        if ($http_cookie ~* "(; )?devredirect=harald") {
            proxy_pass              http://10.0.0.20;
            break;
        }
        ...
    }
    ....
}


OSX 10.5.x + PHP + pecl_ncurses


After several days playing around with newt i am quite disappointed to come to a point where i have to say, that newt has not only a very limited documentation (i think there's only documentation of about 50% of the newt functionality) -- which would not be that of a problem, though. The bigger problem is, that things are very unstable. "Segmentation faults" reproducable on different operating systems and platforms -- not sure though, if the problem is newt or pecl_newt. However: this is definitly not what i want. So, let's move on to something better.

I had a look at the apple's opensource repository the other day and stumbled over a ncurses package. I had problems with the normal gnu ncurses package before, it would not install but hang when building the terminfo database. With the ncurses package i found at apple's opensource repository, i get it to install. Here is the howto:

Note: The first three steps below are for osx 10.5.x. For osx 10.6.6 simply download ncurses 5.7 and build it using ./configure --with-shared.

  1. Download ncurses from apple's opensource repository. You can find it by clicking on the link of the Mac OSX version you are running, eg. 10.5.8.
  2. Unpack the package und you get a directory called something like "ncurses-21" or so. Next cd into this direktory and run make and sudo make install. If make complains about a missing directory /tmp/ncurses/Build, just create it mkdir -p /tmp/ncurses/Build and start again.
  3. Now an intermediate build should have been generated. Next cd /tmp/ncurses/Build, make and sudo make install. Now ncurses should have been successfully installed.
  4. Ready to install the ncurses php extension by either executing sudo pecl install ncurses or downloading ncurses from pecl.php.net and building manually.
  5. Don't forget to add ncurses.so to your php.ini

Now everything should be ready to start writing ncurses apps -- however: for me it sill did not work. The simplest ncurses app resultet in the following error message:

Error opening terminal: vt100.

What's going on? dtruss might be your friend to figure this out. Execute the following command, where example.php is your ncurses app you want to run: sudo dtruss php example.php. You should see output like:

[...]
lstat("/Users/harald\0", 0xBFFFE41C, 0xFFFFFFFFBFFFEFC8)                 = 0 0
lstat("/Users\0", 0xBFFFE2EC, 0xFFFFFFFFBFFFEFC8)                = 0 0
ioctl(0x3, 0x4004667A, 0xBFFFF17C)               = -1 Err#25
ioctl(0x3, 0x402C7413, 0xBFFFF150)               = -1 Err#25
fstat(0x3, 0xBFFFF1B0, 0xBFFFF150)               = 0 0
mmap(0x0, 0x226, 0x1, 0x2, 0x3, 0x100000000)             = 0x17FA000 0
lseek(0x3, 0x0, 0x1)             = 518 0
munmap(0x17FA000, 0x1F3)                 = 0 0
close_nocancel(0x3)              = 0 0
ioctl(0x1, 0x4004667A, 0xBFFFECEC)               = 0 0
stat("/Users/harald/.terminfo\0", 0xBFFFEC00, 0xBFFFECEC)                = 0 0
access("/Users/harald/.terminfo/73/vt100\0", 0x4, 0xBFFFECEC)              = -1 Err#2
stat("/usr/local/share/terminfo\0", 0xBFFFEC00, 0xBFFFECEC)              = -1 Err#2
write_nocancel(0x2, "Error opening terminal: vt100.\n\0", 0x24)            = 36 0

You can see, that ncurses is looking for a file vt100 in a terminfo directory and fails looking at /usr/local/share/terminfo. I indeed had no directory /usr/local/share/terminfo but a directory /usr/share/terminfo. I was not able to figure out, where i could specify the correct directory configuration for this, so i just created a symlink: sudo ln -snf /usr/share/terminfo /usr/local/share/terminfo. After creating the symlink, i was able to execute my example ncurses application.



OSX 10.5.x + PHP + pecl_newt


I always wanted to provide nice user interfaces for some of my commandline tools written in PHP, but was not able to solve one problem until recently. The big problem when writing user interfaces for commandline utilities written in PHP is: ... what library to use for actually building the user interface?

  • You could try to write your own library using ANSI escape sequences -- but your user interfaces would either be very limited or it would be a hell of work to write an extensive library providing more than just the basics.

  • You could try to get the ncurses extension to work -- i failed.

  • You could try to get the newt extension to work -- i failed ...

... until recently. Every once in a while i tried to dig up more information of how to get newt to work, because this is the library i would prefer over the other solutions. However, it did not compile on my system. Recently i searched again and was able to dig up a patch for newt-0.5.22.11. So, here are the steps to get things work:

  1. Download newt 0.52.11 and extract it.

  2. Download the patch for newt from the page above and apply it as described in the short tutorial provided on that page.

  3. Download slang, which is required by newt. I decided to download the latest snapshot (pre2.2.3-60) and things worked just fine with it. Extract and build it -- i did not use any special flags for this.

  4. Download popt, which is required for newt. I decided to download popt-1.16, which seems to be the latest release and can be found at the bottom of the download page. Extract and build it -- i did not use any special flags for this.

  5. After installing slang and popt and patching newt, you should now be able to build and install newt.

  6. Download pecl_newt, or install it using pecl install .... Don't forget to add newt.so to your php.ini.

pecl_newt provides some examples which should be executed to verify, that installation succeeded.



proftpd + mod_sql: solving "slow login" problem


I had a very annoying problem with proftpd, which seems a common one at first sight: slow login and the problem, that a lot of ftp clients out there have a low timeout setting configured. The problem is that googling "slow connection" or "slow login" in combination with "proftpd" led me in a totally wrong direction. A lot of people seem to have a problem with DNS lookups, which can be easily fixed by adding ...

UseReverseDNS off
IdentLookups  off

... to the configuration file, to turn of any DNS lookups. But this did not change anything for me. Running a ftp client in debug mode it turned out, that the authorization itself took a very long time, which led to a timeout with most ftp clients:

air:~ harald$ ftp -d ftp.xxxxxxxxxx.de
Connected to ftp.xxxxxxxxxx.de.
220 xxxxxxxxxx FTP Server
ftp_login: user `' pass `' host `ftp.xxxxxxxxxx.de'
Name (ftp.xxxxxxxxxx.de:harald): 
---> USER harald
331 Password required for harald
Password: 
---> PASS XXXX 
...

The password was send, and than the ftp client had to wait 10 seconds and longer for a respone. Lot's of ftp clients have a timeout of less than 10 seconds, which results in a timed out connection for such a long response time.

After googling for quite some time without finding anything useful on this topic -- besides the DNS lookup problem -- i delved deeper into to the proftpd documentation and found a howto which gave me some hints of how to speed up ftp login.

As it turned out the problem was my SQLAuthenticate directive, which i just copied from the example configuration file of mod_sql. The configuration was set to:

SQLAuthenticate users userset

The problem with this configuration is, that the userset switch seems to be very, very expensive. I still don't know, why this switch is set in the configuration -- the documentation contains no useful examples of when to use / when to avoid this switch, but eventually i found a forum post of a proftpd maintainer, where he tells, that the userset switch is not necessary to be configured. After changing above configuration to ...

SQLAuthenticate users

... login is fast as hell. I'm still curious why the switch was there ...



Setting up Master-Slave replication using xtrabackup


In a previous blog entry i described a method of how to setup master-slave replication with mysql. In steps #4 and #5 i used mysqldump and mysql-client for creating a database dump on the master and importing it on the slave. The problem with this approach is, that the database tables are locked, as long as the dump is running. For small databases this might not be a problem, but as data grows, the time to create a dump takes longer and longer. @work we apparently reached some critical level -- mysqldump ran hours and hours and would probably still run, if i had not stopped it already.

Luckily there are more suitable tools for large databases available. Innodb hot backup and xtrabackup. I've decided to go with xtrabackup, because it's open-source, free and actively developed. Innodb hot backup is closed-source and not for free ;-).

The following steps are ment to replace steps #4 and #5 of my previous blog post.

1. building xtrabackup

For Linux i had to build xtrabackup from the source package, because there was no binary package available for my architecture -- it's very easy, though:

harald@master:~/xtrabackup-0.9.5rc$ automake -a -c
...
harald@master:~/xtrabackup-0.9.5rc$ ./configure
...
harald@master:~/xtrabackup-0.9.5rc$ make
...
harald@master:~/xtrabackup-0.9.5rc$ cd innobase/xtrabackup
harald@master:~/xtrabackup-0.9.5rc/innobase/xtrabackup$ make
...
harald@master:~/xtrabackup-0.9.5rc/innobase/xtrabackup$ sudo cp \
    innobackupex-1.5.1 /usr/local/bin
harald@master:~/xtrabackup-0.9.5rc/innobase/xtrabackup$ sudo cp \ 
    xtrabackup /usr/local/bin

Needless to say, that xtrabackup needs to be deployed on every database server.

2. creating a database dump

After successfully building and installing xtrabackup, taking a database dump is very easy:

root@master:~# innobackupex-1.5.1 --user=... --password=... \
    --defaults-file=... --databases="..." .

The command innobackupex-1.5.1 takes the following parameters:

--user
username to use for database connection
--password
password to use for database connection
--defaults-file
this parameter is required, if the my.cnf configuration file is not located at /etc/my.cnf
--dabases
space-separated list of databases to backup
.
destination directory to save dump to

Dumping the database with xtrabackup is incredible fast compared to mysqldump. With xtrabackup it's just a matter of minutes:

real    4m15.614s
user    0m11.710s
sys     0m14.960s

If xtrabackup was successful, it should have created a subdirectory which name is the current date/time, with all required files in it. The directory can now be copied to the slave:

root@master:~# scp -r 2010-03-02_15-02-24 root@xx.xx.xx.xx:~

3. Setting up the slave

The first thing to do on the slave is applying the binary log files to the database dump:

root@dbslave1:~# innobackupex-1.5.1 --apply-log 2010-03-02_15-02-24

...

100302 14:29:56  innobackupex: innobackup completed OK!

Innobackupex will show the message above, if everything was OK. Next task is to copy the database dump to it's new location on the slave. innobackupex is doing everything for you:

root@dbslave1:~# innobackupex-1.5.1 --copy-back 2010-03-02_15-02-24

...

100302 14:29:56  innobackupex: innobackup completed OK!

xtrabackup should now have copied the dump to the mysql data directory. It's a good idea to check the user and owner of the copied files and adjust them, when needed.

Last step is to start the replication. All information required to do so ist stored in the file xtrabackup_binlog_info:

root@dbslave1:~# cat 2010-03-02_15-02-24/xtrabackup_binlog_info
mysql-bin.000331	54249842

With this information available the replication can be set up as described in step #6 of my previous blog post.



New xserve arrived ...


Last year we purchased our first xserve. Yesterday our second one arrived at our office. Today "he's" standing on my desk. Tomorrow, when we moved him to our data center, he will help encoding videos ...








MySQL master / slave replication


There are tons of tutorials about setting up master / slave replication for MySQL. Here are my own quick notes:

1. Master: /etc/mysql/my.cnf

[mysqld]
server-id = 1
log_bin   = /var/log/mysql/mysql-bin.log
expire_logs_days = 1
max_binlog_size  = 100M

2. Slave: /etc/mysql/my.cnf

[mysqld]
server-id = 2
log_bin   = /var/log/mysql/mysql-bin.log
expire_logs_days = 10
max_binlog_size  = 100M

3. Master: granting privileges for slave user on database master

GRANT REPLICATION SLAVE ON . 
TO '<slave_username>'@'<slave_ip>' 
IDENTIFIED BY '<slave_password>';

4. Master: creating database dump

Start mysql console as database root and enter the following command:

FLUSH TABLES WITH READ LOCK;

DON'T shut down the mysql client, otherwise the table lock is lost. Open a second shell to the database master and enter the following command on commandline:

mysqldump -u root -p... --databases ... --opt > masterdump.sql

Next, switch back to your mysql console and enter the following command:

SHOW MASTER STATUS;

The output will look something like:

mysql> show master status;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000004 | 40140874 |              |                  | 
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)

mysql> 

Write down "File" and "Position" ... you will need it later for starting replication.

Now you can unlock the tables:

UNLOCK TABLES;

5. Slave: import database dump

Copy masterdump.sql to the slave server and import the database:

mysql -u root -p... < masterdump.sql

This may take quite some time ...

6. Slave: start replication

Start mysql client on slave and enter the following commands:

CHANGE MASTER TO 
MASTER_HOST='<master_host>', 
MASTER_USER='<slave_username>', 
MASTER_PASSWORD='<slave_password>', 
MASTER_LOG_FILE='<mysql-bin file name you've written down in step 4>', 
MASTER_LOG_POS=<master position you've written down in step 4>;

START SLAVE;

long time -- no update


I'm currently preparing to switch my blog software -- again. After using wordpress and serendipity for quite some time, i came to the conclusion, that i will only be satisfied with my own blog software. Therefore i'm currently developing something based on the php5 framework i developed for work. I also decided to switch language ... now i can practice my english and increase the audience of people, who won't be interested of what i am writing ;-).



cookie based redirect mit nginx


Vor einiger Zeit habe ich einen Tip beschrieben, wie man einen cookie-based redirect mit dem LightTPD konfigurieren kann. Das Problem an der Sache ist für mich, dass das für diesen Zweck verwendete Modul mod_proxy von LightTPD in dem von mir eingesetzen Entwicklungszweig (1.4.x) nicht SSL kompatibel ist und dementsprechend HTTPS Verbindungen fehlschlagen.

Seit einiger Zeit schon habe ich den Web- und (Reverse-) Proxy-Server nginx im Auge. Auch mit diesem Server ist es möglich einen Redirect einzurichten, so wie ich ihn brauche. Und: nginx unterstützt an dieser Stelle SSL!


server {
    listen          80;
    server_name     *.devcenter.int;

    proxy_redirect      off;
    proxy_set_header    Host            $host;
    proxy_set_header    X-Real-IP       $remote_addr;
    proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;

    location / {
        if ($http_cookie ~ "(; )?devredirect=harald") {
            proxy_pass              http://10.0.0.20;
            break;
        }
        if ($http_cookie ~ "(; )?devredirect=markus") {
            proxy_pass              http://10.0.0.22;
            break;
        }

        ...
    }
}


server {
    listen          443;
    server_name     ....devcenter.int;

    ssl                     on;
    ssl_certificate         /etc/nginx/....crt;
    ssl_certificate_key     /etc/nginx/....key;

    proxy_redirect      off;
    proxy_set_header    Host            $host;
    proxy_set_header    X-Real-IP       $remote_addr;
    proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;

    location / {
        if ($http_cookie ~ "(; )?devredirect=harald") {
            proxy_pass              https://10.0.0.20;
            break;
        }
        if ($http_cookie ~ "(; )?devredirect=markus") {
            proxy_pass              https://10.0.0.22;
            break;
        }

        ...
    }
}



Hitzeprobleme beim iMac


Die iMacs leiden offenbar unter Hitzeproblemen. Bei mir machte sich das seit ein paar Wochen bemerkbar, als mein weisser 24" iMac (von 2006) anfing nach längerem Arbeiten Grafikfehler zu produzieren. Angefangen hat es mit horizontalen Linien über den Bildschirm, zum schluss waren es Verzerrungen, merkwürdige Ränder an den Programmfenstern, verschwundene Dock-Icons und Pixel-Müll auf dem Bildschirm. Im Apfeltalk Forum gab man mir den Rat den Apple Hardware Test, der sich auf der ersten Installations DVD des iMacs befindet, zu benutzen, um Hardwarefehler auszuschliessen, da ähnliche Symptome offenbar auch bei Leuten auftraten, deren iMac-Grafikkarten defekte VRams aufwiesen.

Der Apple Hardware Test verlief glücklicherweise fehlerfrei. Ich hatte auch schon eine Vermutung, dass es möglicherweise ein Hitzeproblem sein könnte: der iMac ist superleise, einen Lüfter hört man praktisch nicht, da die Drehzahl von Haus aus sehr gering eingestellt ist. Ich habe mir also das Programm Temperaturmonitor besorgt und sah hier meinen Verdacht als durchaus begründet, da der Netzteilsensor über 80° gemessen hat und die Grafikkartensensoren zwischen 50° und über 60° anzeigten.

Beim Googeln entdeckte ich einen Artikel in einem Blog, der meinen Verdacht dann bestätigte: die Temperaturen sind viel zu hoch. In diesem Artikel wird das Programm smcFanControl erwähnt, das ich mir sogleich installiert habe. Über diese Software lassen sich die Drehzahlen dreier Lüfter im iMac einstellen. Ich habe mir im smcFanControl nun ein Profil angelegt, mit dem ich die Lüfter auf maximale Drehzahl laufen lassen kann. Damit konnte ich die Temperaturen laut Sensoren um 20° bis 30° absenken und siehe da -- keine Grafikfehler mehr.

Ich bin erleichtert, dass es offenbar "nur" ein Hitzeproblem ist und kein Hardware-Defekt. Mit verschiedenen Profilen im smcFanControl kann man sich optimale Lüfterdrehzahlen zusammenstellen. Ich habe festgestellt, dass die Grafikfehler bei mir bei den Werten "Grafikprozessor Kühlblech 1 > 40° und Grafikprozessor Temperaturdiode > 55°" (etwa) auftauchten. Deshalb habe ich nun ein Profil, welches die Werte konstant darunter hält. Das funktioniert eigentlich ganz gut. Ich bin noch nicht sicher, ob das ein Dauerzustand sein kann -- die Lüfterdrehzahlen sind zwar immernoch so niedrig, dass die Geräuschentwicklung nicht wirklich störend ist, evtl. aber ist der iMac innen verstaubt und man müsste ihn mal zum Reinigen öffnen. Aber für den Moment bin ich zufrieden -- so wie es ist.



"Ports of Call" auf dem iPhone


Wem der Spieleklassiker "Ports of Call" (POC) nichts sagt, ist entweder zu jung und / oder hat die Zeit der Homecomputer nicht erlebt, hatte keinen Amiga oder kannte niemanden der einen Amiga sein Eigen nannte. "Ports of Call" ist ein Handelsspiel, in dem es darum geht eine Reederei aufzubauen und Handel über Seewege zu treiben. Das Spiel war seiner Zeit aber irgendwie voraus, denn es war nicht nur ein Handelsspiel, sondern beinhaltete auch einen Simulator. So konnte man sein Geschick versuchen und die Hafen ein- und ausfahrten selbst steuern. Oder man fuhr eine Route mit Eisbergen und musste diese umschiffen.

Seit geraumer Zeit schon wird das Spiel weiterentwickelt. Inzwischen gibt es Portierungen für aktuelle Windows Versionen und sogar einen richtigen 3D Simulator.

Gestern erschien nun die iPhone / iPod Touch Portierung von POC und ich dürfte einer der ersten gewesen sein, die das Spiel im App Store erworben haben. Bei der Portierung handelt es sich um die alte Classic Version und mit entsprechendem Charme kommt sie auch daher. Bedienen lässt sich das Spiel natürlich über den Touch Screen. Der Bildschirm wird leider nicht voll ausgenutzt, da dort wo es nötig ist, zusätzlich Kontrollflächen eingeblendet werden. So wurden z.b. Schaltflächen zum Manövrieren des Schiffes an den Seitenrand gepackt. Aufgrund der Tatsache, dass die Kontrollflächen des Spiels ansonsten nicht angepasst wurden, sind diese gemessen an der Bildschirmauflösung teilweise recht klein. Deshalb weiss ich auch nicht ob es an meinen grossen Fingern liegt oder ob es tatsächlich so ist, dass die Schaltflächen nicht immer beim ersten "Klick" bzw. Anfassen reagieren.

 

 

Alles in allem macht es selbst nach 20 Jahren, die das Spiel inzwischen auf dem Markt ist, noch spass damit zu spielen. Auch auf dem iPhone macht es Spass ein Schiff aus dem Hafen zu navigieren oder den Riffen in schwierigem Gewässer auszuweichen. Das Spiel ist mit seinen 3,99 € sicher nicht eines der günstigen im App-Store, dafür bekommt man aber einen echten Klassiker, der nichts von seinem Charme verloren hat und der für viele Stunden Spielspass sorgen kann.



ORMs sind doof


ORM

Wikipedia sagt dazu:

Objektrelationale Abbildung (englisch object-relational mapping, ORM) ist eine Technik der Softwareentwicklung, mit der ein in einer objektorientierten Programmiersprache geschriebenes Anwendungsprogramm seine Objekte in einer relationalen Datenbank ablegen kann. Dem Programm erscheint die Datenbank dann als objektorientierte Datenbank, was die Programmierung erleichtert. [...]

Inzwischen bringt ja so ziemlich jedes PHP Framework seine eigene ORM Implementierung mit, es gibt aber auch einige Framework-unabhängige ORM Implementierungen. Ich habe mir in den letzten Jahren immer mal wieder verschiedenste ORM Implementierungen angesehen -- immer dann, wenn in mir der Wunsch nach einer objektorientierten Zugriffsweise auf meine Datenbanken aufkam. Leider jedoch konnte mich bisher keine ORM Implementierung überzeugen.

ORMs sind doof

Auch in mir kommt immer mal wieder der Wunsch auf objektorientiert auf meine Datenbanken zuzugreifen, da dies den Zugriff auf einzelne Datensätze -- Objekte -- erheblich vereinfacht. Jedoch -- zu welchem Preis wird diese Vereinfachung erkauft?

Modellierung

Ich modelliere meine Datenbanken schon seit Jahren mit dem ER Modeller dbWrench. Das ist meiner Meinung nach super komfortabel. Ich sehe auf einen Blick all meine Tabellen und die Abhängigkeiten bzw. Verknüpfungen zwischen einzelnen Tabellen. Über die Funktion "Forward Engineering" kann dbWrench mein Datenbankschema in der Datenbank immer aktualisieren. Da ich bei MySQL den Tabellentyp InnoDB verwende, sind auch in der Datenbank sämtliche Verknüpfungen festgehalten und liessen sich z.b. über die INFORMATION_SCHEMA Tabelle leicht auslesen.

Nun ist es leider so, dass offenbar so ziemlich jede ORM Implementierung die Datenbankdefinition auf Ihre Weise bekommen möchte. Da muss man entweder seitenweise XML oder YAML Konfiguration, oder gar ellenlangen PHP Code schreiben -- nur um der Anwendung eine Information bekannt zu geben, die eigentlich exakt so schon in der Datenbank vorhanden ist?

Abstraktion

Wie weit muss man die Datenbankzugriffe abstrahieren? Nun, es gibt da sicherlich die verschiedensten Anforderungen. Ich denke bei der Entwicklung von Unternehmenssoftware kann man die Anforderungen ziemlich genau spezifizieren. Man entscheidet sich zu einem gewissen Zeitpunkt für ein bestimmtes Datenbankprodukt. Normalerweise wird diese Entscheidung nicht nach wenigen Monaten oder Jahren über den Haufen geworfen -- es sei denn es gibt sehr triftige Gründe dafür.

Deshalb bin ich der Meinung, dass die Abstraktion nicht so weit gehen muss, dass sämtliche Datenbankzugriffe abstrahiert werden und für beliebige Datenbanksysteme geeignet sind. Im Gegenteil: ich entscheide mich ja nicht für eine bestimmte Datenbank nur aus Kostengründen, sondern auch, weil diese vielleicht Features mitbringt, die ein anderes Datenbanksystem nicht unterstützt.

So erweitert z.b. MySQL den SQL Standard um eigene spezifische Befehle, die es in anderen Datenbanken nicht gibt, die aber sehr praktisch sind. Das ist kein Alleinstellungsmerkmal von MySQL. Beispiel: Hätte ich mich für Oracle entschieden, wäre ich doch dumm, würde ich zum Abbilden / Abfragen von Hierarchischen Strukturen nicht CONNECT BY verwenden -- nur weil dies nicht Teil des SQL Standards ist und dies so mit keiner anderen Datenbank funktioniert.

Nur: keine ORM Implementierung kann auf diese einzelnen Datenbankfeatures eingehen -- womit ich beim nächsten Punkt angelangt wäre.

Abfrage

Das grösste Manko aller (PHP) ORM Implementierungen ist meiner Meinung nach die Abfrage einer Datenbank. Ich gebe zu: ich mag SQL -- es gibt mir das passende Werkzeug zum Abfragen einer relationalen Datenbank in die Hand -- es wurde zu diesem Zweck entwickelt! Ich schreibe gern SQL, da es strukturiert und übersichtlich ausschaut und mich schnell zum Ziel führt. Ich gebe weiterhin zu: Ich nutze auch gern MySQL spezifische SQL Features -- aus den oben genannten Gründen.

Nun ist es jedoch so, dass die ORM Implementierungen in der Regel den Zugriff soweit abstrahieren, dass -- normalerweise -- kein SQL mehr geschrieben wird. CONNECT BY und ähnliche Dinge wären damit Unmöglich. Heutzutage hat sich folgende Schreibweise zum Erstellen von Datenbankabfragen etabliert:

$dbo
    ->select(array(
        'media.media_id', 'media.media_name', 'member.member_name', ...
    ))
    ->from('media')
    ->join('member', 'member.member_id = media.member_id')
    ->where('media.category_id = ?')
    ->order('media.media_id')
...

Ich bin kein Fan einer solchen Schreibweise:

  1. Es ist kein SQL ;-)
  2. Es ist wesentlich mehr Aufwand als beim Schreiben von SQL erforderlich
  3. Ich kann keine Datenbankspezifischen SQL Erweiterungen verwenden
  4. Ich habe keine Kontrolle darüber, welchen SQL Code die ORM Implementierung daraus generiert
  5. Es liegt in der Natur der Sache, dass ein derartiges Konstrukt niemals auch nur annähernd so performant sein kann wie ein simples SQL Statement übergeben an die Datenbank
  6. Ich kann das Statement nicht per Copy / Paste zwischen meinem Datenbank Client und der Anwendung hin und her kopieren -- praktisch, wenn man das ganze erstmal testen will
  7. Wenn ich den Datenbanktreiber einer nicht-relationalen Datenbank hinterlege, weil ich mich z.b. entscheide statt MySQL MongoDB anzusprechen, wird diese Schreibweise ohnehin ad Absurdum geführt. (Nur als beispiel -- ich weiss nicht, ob irgendeine PHP ORM Implementation überhaupt nicht-relationale Datenbanken unterstützt)

Natürlich bietet so ziemlich jede ORM Implementierung einen Fallback zur Herkömmlichen Absetzung von SQL Anfragen ohne ein Objekt-Mapping. Nur, wenn ich damit an einer Stelle in meiner Anwendung anfange: warum dann überhaupt eine derartige Abstraktion nutzen?

Fazit

Meiner Ansicht nach erkauft man sich den konsequenten Einsatz eines ORM zu einem zu hohen Preis. Deshalb habe ich den Einsatz eines solchen für mich immer wieder verworfen. Mein Wunsch wäre ein SQL -> Objektmapper. D.h.: Ich schreibe SQL, zurück bekomme ich Objekte, mit denen ich weiterarbeiten kann ...



MongoDB + PHP + OSX


Ich spiele gerade ein wenig mit Mongo DB. Da ich das die Tage sicherlich noch mal gebrauchen kann, mache ich mir hier mal ein paar wichtige Notizen zur Installation:

  • Der Aktuelle PHP Treiber ist nicht kompatibel zum aktuellen stable Release (0.8.0) auf der Mongo DB Seite. Deshalb muss ein daily-build von Mongo DB heruntergeladen werden. Am besten lädt man hier das Paket inkl. aller Tools und Treiber (beinhaltet auch den Source der PHP Erweiterung). Mongo DB also herunterladen und irgendwohin installieren (z.b. /opt/mongo).
  • Zum Compilieren des PHP Treibers benötigt man die Boost C++ Libraries. Beim Compilieren der Boost Libraries wurden die Namen der Bibliotheken derart angelegt, dass sie beim Compilieren des PHP Treibers von Mongo DB nicht gefunden werden konnten. Deshalb habe ich nach dem ./configure --prefix=/opt/boost von Boost die Datei Makefile editiert und die Zeile BJAM_CONFIG= nach BJAM_CONFIG='--layout=system' geändert.
  • Nachdem Boost Installiert wurde, lässt sich nun der Mongo DB Treiber compilieren. Bei mir befindet sich der PHP Treiber unterhalb des Verzeichnisses /opt/mongo/drivers_and_tools/mongo-php-driver. Ein phpize, ./configure --with-mongodb=/opt/mongo/ --with-boost=/opt/boost/, make, make install und der Treiber ist gebaut und installiert.

Eigentlich ganz einfach ... aber ich vergesse es sonst garantiert wieder :-)



cookie based redirect mit lighttpd


Alle unsere Entwickler haben eine komplett eigene Entwicklungsumgebung inkl. WebServer installiert. Manchmal ist es praktisch den aktuellen Entwicklungsstand am Checkout eines Entwicklers zu sehen. Anstatt für jedes Projekt und jeden Entwickler eine interne Subdomain einzurichten, kann man viel praktischer einen zentralen Reverse Proxy nutzen, der anhand einer Cookie Einstellung den Host des jeweiligen Entwicklers auswählt:

$SERVER["socket"] == "10.0.0.20:80" {
    ...
    $HTTP["host"] =~ "^.*\.devcenter.int$" {
        $HTTP["cookie"] =~ "(; )?devredirect=harald" {
            proxy.server = (
                "" => (("host" => "10.0.0.20", "port" => 8080))
            )
        }
        $HTTP["cookie"] =~ "(; )?devredirect=markus" {
            proxy.server = (
                "" => (("host" => "10.0.0.22", "port" => 8080))
            )
        }
    }
}    

Die oben gezeigte Konfiguration installiert einen Reverse Proxy für Subdomains von 'devcenter.int' -- z.B. 'clipdealer.devcenter.int'. Über das Cookie 'devredirect' kann spezifiert werden, welcher Host angesprochen werden soll.