Letztens hat mich ein aufmerksamer Leser nach einer mittleren Odyssee (jaja, ich werde mal sowas wie ein Impressum einbauen) kontaktiert um mir mitzuteilen, daß einer meiner Blogposts einen Skriptfehler beim Klick auf einen Link liefert. Ich hab mir mal etwas Zeit genommen, das ganze zu analysieren.
Konkret geht es um folgende Fehlermeldung (nur der interessante Teil):
ERROR: duplicate key violates unique constraint "serendipity_exits_pkey"
Der Fehler zeigt die Verletzung der Eindeutigkeitsbedingung "serendipity_exits_pkey" an. Eine Eindeutigkeitsbedingung ist ein Konstrukt in einer Datenbank, welches verhindert, daß ein bestimmter Schlüssel in einer Tabelle doppelt vorkommt. Der spezielle Name dieser Bedingung hier zeigt, daß der Primary Key der Tabelle serendipity_exits gemeint ist. Um das zu verstehen muss man sich ein wenig tiefer mit dem Aufbau der Software beschäftigen: Externe Links in einem Blogpost zeigen nicht direkt auf das Ziel, sondern rufen die Datei exit.php auf, welche dann zum passenden Ziel weiterleitet. Ziel der Aktion ist es, nochmal eine Statistik erfassen zu können, welche Links im Blog besonders häufig angefasst werden. Dazu wird der Klick auf einen Link in einer Datenbank gezählt (eben in der Tabelle serendipity_exits). Nun hat diese einen Primary Key, also eine Sammlung von Spalten, die zusammen jeweils den eindeutigen Schlüssel eines Eintrags ergeben. Für diese Datei ist das bisher das Tupel (day, host, entry_id). Eindeutig identifiziert ist ein Link also dann, wenn man Tag, Zielrechner und Quelleintrag im Blog des Links kennt. Der geneigte Leser wird hier evtl. schon einen Fehler finden: was passiert eigentlich, wenn zwei verschiedene externe Links in einem Artikel auf einem Host liegen und am selben Tag geklickt werden? Richtig, Serendipity versucht, beide einzutragen, da sich der path (also der Teil nach dem Host) unterscheidet und fällt damit natürlich prompt auf die Nase, weil der Primärschlüssel nicht zweimal vergeben werden kann.
Ursache des Fehlers ist hier eine unterschiedliche Auffassung der Datei exits.php und der Datenbank über den Begriff der Eindeutigkeit eines Eintrags. Die Auffassung der Datenbank ist fehlerhaft, denn zu einem Link gehört nicht nur der Host, sondern auch der Pfad (erst dann hat man ja das konkrete Dokument). Ergo gilt es, der Datenbank das passende Verfahren beizubringen.
Dazu verwirft man ersteinmal den bisherigen Primary Key, da man den nicht ändern und auch keine zwei definieren kann. Man verbindet sich zu seiner Serendipity-Datenbank und gibt folgendes ein:
ALTER TABLE serendipity_exits DROP CONSTRAINT "serendipity_exits_pkey";
Danach funktioniert das ganze zwar theoretisch wieder, läßt aber zuviel Raum für fehlerhafte Doppeleintragungen. Ergo schränkt man das ganze wieder ein, indem man einen neuen Primärschlüssel anlegt:
ALTER TABLE serendipity_exits ADD PRIMARY KEY (entry_id,day,host,path);
Das ganze kann im laufenden System passieren und zerstört keine Daten. Es ist natürlich immer besser, man hat ein Backup rumliegen (sollte man ja aber sowieso). Nach dem Fix können in einem Artikel wieder ohne Probleme Links verwendet werden, die auf denselben Host zeigen, ohne daß die Nutzer dadurch immer nur auf einen klicken können und alle anderen Fehler liefern.
Den Fehler einschließlich Fix habe ich gestern bei den Serendipity-Entwicklern eingeliefert und bereits eine Antwort von Garvin Hicking. Die nächste Version der Software wird diesen Fehler korrigieren. Bis dahin kann man den vorgeschlagenen Fix von Hand einspielen.