Hipchat Sort Contacts

Ok, it only works for the hipchat web app, but here is a bookmarklet to sort your contacts.

Werbeanzeigen

Svn post-commit auto update hook ohne Rechteprobleme (mal etwas anders)

Ein typisches Szenario im Webdevelopement ist ein Subversion Repository und ein Entwicklungsserver der automatisch geupdated werden soll, sowie ein Entwickler etwas committed. Das hierfür notwendige post-commit hook läuft mit den Rechten des committenden Users, so dass alle neuen Dateien von den anderen nicht mehr geschrieben werden dürfen… Keine Ahnung, wieso es dafür keine elegante Lösung gibt. Im Netz findet man häufig als Workaround ein compiliertes C Programm, welches immer als gleicher User ausgeführt wird. Es geht aber auch anders, und zwar so:

Voraussetzung:
– SVN Repository (hier /var/svn/project/trunk), Zugriff mit svn+ssh://
– Webserver (hier mit DocumentRoot in /var/www/project/dev)
– Eine Gruppe für SVN, der alle Entwickler angehören (hier subversion)

Für das Update benötigen wir einen eigenen User mit SSH Schlüsselpaar und Berechtigungen für das entsprechende DocumentRoot, im post-commit hook loggen wir uns einfach per SSH als Update-User auf die lokale Maschine ein und führen das Update durch. Klingt irgendwie nach „von hinten durch’s Auge“, hat aber sogar den Vorteil, dass das ganze auch dann funktioniert, wenn Repository und Webserver tatsächlich auf unterschiedlichen physikalischen Maschinen laufen.

1. User anlegen und SSH Schlüsselpaar erzeugen (ohne Passphrase)

  adduser updater
  addgroup subversion updater
  su updater
  cd ~
  mkdir .ssh
  chmod 700 .ssh
  ssh-keygen -t rsa

2. Public Key zu den authorisierten SSH Schlüsseln hinzufügen

  cp ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys2
  chmod 600 ~/.ssh/authorized_keys2

3. Private Key für alle Entwickler (= alle User in der Gruppe subversion) zugänglich machen

  cp ~/.ssh/id_rsa /var/svn/hooks/id_rsa
  chmod 640 /var/svn/hooks/id_rsa_updater
  chown updater:subversion /var/svn/hooks/id_rsa_updater

4. Document Root anlegen und Projekt erstmalig auschecken

  su root
  mkdir /var/www/project/dev
  chown updater /var/www/project/dev

  su updater
  cd /var/www/project/dev
  svn checkout file:///var/svn/project/trunk/ ./

5. post-commit hook anlegen

   mv /var/svn/hooks/post-commit /var/svn/hooks/post-commit.bak
   echo '#!/bin/sh' > /var/svn/hooks/post-commit
   echo 'ssh -i /var/svn/hooks/id_rsa_updater updater@127.0.0.1 svn up /var/www/project/dev' >> /var/svn/hooks/post-commit
   chmod 755 /var/svn/hooks/post-commit

6. Da beim erstmaligen Ausführen der RSA key fingerprint in den known_hosts abgelegt werden muss, muss das post-commit Script zunächst einmalig von jedem Entwickler in der Konsole ausgeführt werden.

  su sui
  /svn/hooks/post-commit

  The authenticity of host '127.0.0.1 (127.0.0.1)' can't be established.
  RSA key fingerprint is 81:7c:e0:bd:7a:c8:e5:56:0a:c8:7f:95:0c:b1:6a:d0.
  Are you sure you want to continue connecting (yes/no)? yes
  Warning: Permanently added '127.0.0.1' (RSA) to the list of known hosts.
  At revision 74.

Fertig 🙂

Performance Tuning

Es gibt ja unter anderem die SEO-These, dass das google Ranking auch durch die Ladezeiten einer Website beeinflusst wird, und mit diesem Hintergedanken bekam ich neulich beim Blick in die Google Webmastertools wirklich Panik: 3,1 Sekunden, langsamer als 52 % der Websites.
Drei-Komma-Eins Sekunden?! Wtf? Messen die das aus Timbuktu? Inzwischen weiß ich, das die Messungen durch User meiner Website mit installierter Google Toolbar gemacht werden (link). Die Schlussfolgerung lautet also: Die haben alle eine ziemlich miese Internetverbindung, alte Rechner und lahme Browser… naja, und meine Website ist wohl tatsächlich langsamer als jede zweite andere.

Da ich das unmöglich auf mir sitzen lassen kann ist jetzt Extrem-Optimierung angesagt. Wenn ich die Grafik richtig deute muss ich dabei auf eine durchschnittliche Ladezeit von 1,5 Sekunden kommen um in den Olymp der 20% schnellen Websites aufzusteigen. Für den Anfang habe ich mir nur die Startseite vorgenommen (aktuell: 2,3 Sekunden).

Hilfreiche Tools hierfür sind: Page Speed (google) und YSlow (yahoo)

Hilfreichste Optimierungen:

  • CSS+JS Files mit dem YUI Compressor komprimieren, und ausschließlich die für die jeweilige Seite benötigten Scripte zu einer Datei zusammenfassen und einbinden. Auf der Startseite brauche ich z.B. keine jQuery-UI Komponenten – über 100K gespart
  • Bilder zu Sprites zusammenfassen und als Hintergrundgrafiken positionieren – 6 Grafiken in einer Datei zusammengefasst, 5 Requests gespart
  • Bilder gnadenlos komprimieren, png!=png da lässt sich sogar verlustfrei eine ganze Menge sparen. Tool: http://www.punypng.com/
  • Externe JS asynchron laden (hilft zwar glaube ich nicht bei der gemessenen Ladezeit, weil geladen werden sie ja trotzdem – allerdings kann das unter Umständen den Seitenaufbau beschleunigen) oder
  • Externe JS ganz rausschmeißen wenn nicht benötigt. Die Facebook JS-API ist so ein Fall… die tut nämlich die meiste Zeit exakt gar nichts und kostet dafür siginifikant viel Ladezeit. Als praktischer Nebeneffekt wird die Seite ohne die Tags auch wieder w3c konform. Da man sich auf der Startseite dennoch mit FB einloggen können soll, wird die jetzt erst onClick geladen. Das hat zwar auch ein paar hässliche Nebeneffekte, aber dann müssen die bisher noch recht wenigen FB-Connect User halt beim Login-Klick etwas länger warten, PopUps erlauben und unter Umständen zwei mal klicken.
    Der Analytics-Tag ist mir zwar auch noch ein Dorn im Auge, aber ich bin ja leider auch ziemlich heiß auf meine Statistiken 🙂

  • Den Netzwerk-Tab in Firebug beobachten. Bei mir hat z.B. ein .swf onload noch eine Grafik nachgeladen, was als letzter Balken nach einiger Verzögerung deutlich negativ aufgefallen ist. Die Grafik ist jetzt Bestandteil der Website und der Flashfilm legt sich transparent darüber – 200ms gewonnen.

Ich habe damit jetzt tatsächlich einen Page Speed Score von 99 und einen YSlow Score von 94 erreicht.

Prepared Statements sind gar nicht sicherer

Es ist eine weit verbreitete Ansicht, dass SQL-Injections mit Prepared Statements sicher verhindert werden können (es gibt glaube ich auch nicht wenige Leute die glauben Prepared Statements wären dazu da um SQL-Injections zu verhindern) und man diese daher dem klassischen zusammenbasteln von Queries unbedingt vorziehen müsse. Das stimmt so leider nicht, und tragischerweise tappen gerade die Entwickler in die Falle, die sich durch die unreflektierte Verwendung von Prepared Statements besonders sicher fühlen – und zwar genau dann, wenn sie einzelne Variablen nicht bind()’en sondern ungeprüft in’s prepare() packen.

Beispiel aus der Praxis:

$statement = DB::prepare("SELECT c1, c2 FROM t WHERE c1 = :sVal1 LIMIT 0, " . $_REQUEST['limit']);
$statement->bind(':sVal1', $_REQUEST['sVal1'], PDO::PARAM_STR);

…ist natürlich genauso schlimm wie…

mysql_query('SELECT c1, c2 FROM t WHERE c1 = "' . mysql_escape_string($_REQUEST['sVal1']) . '" LIMIT 0, " . $_REQUEST['limit']);

Komfortfeature für Hacker ist dann – zumindest bei PDO vs. mysql_query() – auch noch die defaultmäßige Unterstützung von multiple queries. $_REQUEST[‚limit‘] = ‚; DELETE FROM t;‘; würde somit beim ersten Snippet in der Regel funktionieren, beim zweiten dagegen unter normalen Umständen nicht.

Der einzige Schutz vor SQL-Injections ist und bleibt eben doch das konsequente Escapen/Validieren aller Variablen. Das wiederum ist eine Frage des Entwicklers und nicht die des verwendeten Abstraktionslayers.

Wo geht der ganze Traffic hin?

Die meisten Fehler bei der Entwicklung werden erst unter ganz unerwarteten und unglücklichen Umständen deutlich. Heute hatte ich mal wieder so einen klassischen Fall.

Vor ungefähr einer Woche ist die Datenbankreplikation ausgefallen, das kann passieren und ist auch nicht weiter wild da der Datenbankslave eigentlich nur für Notfälle und Datenbankbackups da ist. Vorgestern hab‘ ich die Replikation wieder angeworfen und der Slave fing auch brav an seine verpasste Woche nachzuholen. Gestern der Blick auf den Datentransfer: 25MBit, doppelt so viel wie sonst und dummerweise auch noch steigend. Ein iftop zeigt mir, dass die Daten auf meinen Backupserver wandern, der die neuen Bilder von instantgallery sichert. Dieser Mechanismus funktioniert folgendermaßen:
Alle Bilder sind in einer Datenbanktabelle eingetragen. Diese Tabelle enthält einen Flag ob das Bild schon gesichert wurde oder nicht. Die Spalte mit dem Flag hat leider keinen Index, das nachträgliche Hinzufügen eines solchen würde bei einer Tabelle mit mehreren Millionen einträgen einen Lock von mehreren Minuten bedeuten, was ich auf dem Livesystem nicht ohne Not machen würde und deshalb nur auf der Replikation gemacht hatte. Das Backupscript wird jetzt alle 5 Minuten per cron gestartet und fragt die Slave-DB welche Bilder neu sind, sichert diese und schreibt anschließend auf die Master-DB dass sie gesichert wurden, welche das wiederum auf den Slave repliziert. Dummerweise hängt der Slave aber gerade eine Woche hinterher und bekommt das erst mit, wenn er diese Woche nachgearbeitet hat. Das Backupscript bekommt also mit jedem Durchlauf eine immer länger werdende Liste von Bildern die zwar schon gesichert sind, aber noch nicht als gesichert markiert sind. Irgendwann ist die Liste so lang, dass das Backupscript sie nicht in 5 Minuten abarbeiten kann, ein zweiter Backupprozess der wieder mehr als 5 Minuten brauchen wird startet und im Handumdrehen laufen da auch schon 5 Backups parallel, fressen Bandbreite, fressen Performance und sichern dabei nur Daten die schon längst gesichert sind.

Dumm gelaufen und hätte man verhindern können wenn
– das Backupscript unabhängig selber merken würde dass die Daten schon gesichert sind
– das Backupscript merken würde, dass es schon läuft oder statt cron einfach immer laufen würde
– vorausschauend gleich einen Index gesetzt und auf dem Master gearbeitet hätte
– statt Flag in der Bildertabelle ein anderes Datenbankdesign gewählt hätte

Naja, wieder was gelernt 🙂

Traffic
Traffic