Managing multiple mod_passenger standalone configurations

If you’re deploying multiple Ruby/Rails apps with passenger, you might end up with different ruby version requirements. Or maybe you want to keep gemsets separate. For whatever reason, you end up running one or more of your apps in a standalone passenger server and proxying them through Apache or nginx.

Or maybe you’re doing mass webhosting with only passenger standalone instances. Whatever floats your boat.

This is the script I hacked together for Toptranslation. Instructions below.

#!/bin/bash

### BEGIN INIT INFO
# Provides:          passenger-standalone
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Easily start or stop standalone passenger instances
### END INIT INFO

# default config
USER=www-data
APP_DIR=
RVM_RUBY=1.9.2
ADDR=127.0.0.1
PORT=3000
ENVIRONMENT=production

if [ -n "$2" -a -r "/etc/passenger-standalone.d/$2" ]; then
  CONFIG="$2"
elif [ -r "/etc/passenger-standalone.d/$(basename $0)" ]; then
  CONFIG="$(basename $0)"
fi

if [ -n "$CONFIG" ]; then
  if [ ! -r "/etc/passenger-standalone.d/$CONFIG" ]; then
    echo "Error: Configuration '${CONFIG}' not found in /etc/passenger-standalone.d/" >&2
    exit 4
  fi
  source "/etc/passenger-standalone.d/$CONFIG"
  if [ -z "$APP_DIR" ]; then
    echo "Error: APP_DIR is not defined."
    exit 5
  fi
  CMD_START="cd $APP_DIR; rvm use $RVM_RUBY; passenger start -a $ADDR -p $PORT -e $ENVIRONMENT -d"
  CMD_STOP="cd $APP_DIR; rvm use $RVM_RUBY; passenger stop -p $PORT"
fi

. /lib/lsb/init-functions
case "$1" in
  start)
    echo "Starting passenger for ${CONFIG}"
    echo "[$USER] $CMD_START"
    su - $USER -c "$CMD_START"
    ;;
  stop)
    echo "Stopping passenger for ${CONFIG}"
    echo "[$USER] $CMD_STOP"
    su - $USER -c "$CMD_STOP"
    ;;
  *)
    echo "Usage: $0 start|stop [config]" >&2
    exit 3
    ;;
esac

To use, first put it into /etc/init.d/passenger-standalone. Then, for each standalone instance you want to start, create a configuration file in /etc/passenger-standalone.d/ with a sensible name. For example, here’s a redmine configuration:

# /etc/passenger-standalone.d/redmine 
USER=www-data
APP_DIR=/opt/redmine
RVM_RUBY=1.8.7-p330
ADDR=127.0.0.1
PORT=3081
ENVIRONMENT=production

And here is the corresponding VirtualHost for Apache:

<VirtualHost *:80>
  ServerName redmine.example.com
  DocumentRoot /opt/redmine/public

  PassengerEnabled Off
  ProxyRequests Off
  <Proxy *>
    Order deny,allow
    Allow from all,
  </Proxy>
  ProxyPass / http://127.0.0.1:3081/
  ProxyPassReverse / http://127.0.0.1:3081/
</VirtualHost>

Now, when you reload apache with that config and try to access redmine.example.com, you will get an Error 500 because the passenger instance is not running yet. To start it, simply run /etc/init.d/passenger-standalone start redmine, OR symlink /etc/init.d/passenger-standalone to /etc/init.d/redmine and run /etc/init.d/redmine start.

To automatically run the script in the appropriate runlevels, symlink it as described, then run update-rc.d redmine defaults. This will set up the appropriate symlinks.

Update: Fixed a backtick that wasn’t supposed to be there.

On computers

The most amazing achievement of the computer software industry is its continuing cancellation of the steady and staggering gains made by the computer hardware industry.
      — Henry Petroski

Notizen

  1. Ich bin verliebt. Und eventuell sexsüchtig.
  2. Winter ist mittlerweile eine Naturkatastrophe und nicht nur eine Jahreszeit.
  3. Der neue Europabahnhof in Saarbrücken ist eine Ausgeburt der hässlichsten Oberflächlichkeit, die ich seit einer Weile gesehen habe, inklusive Warteräumen, die wie Todeszellen aussehen.
  4. Es gibt Micro-USB Typ A und B, und man will eigentlich immer Typ B (angeschrägte Stecker), Typ A nutzt nichts, aber man kann trotzdem Kabel ohne “Typ A”/”Typ B”-Hinweis kaufen und sich damit Buchsen ruinieren.
  5. WRT54GL > Fritz!Box WLAN 3270. Endlich WLAN im ganzen Haus.
  6. Die Telekom ist immer noch nicht willens, DSL-Anschlüsse mit in vorgegebenem Rahmen frei aushandelbarer Geschwindigkeit zu schalten, jedenfalls nicht, wenn man seit Jahren loyaler Kunde ist. Statt dessen wird auf Leitungen, die 4MBit/s schaffen könnten, nur T-DSL 768 (kBit/s) geschaltet. Man kann da nichts machen, das System, Sie verstehen.
  7. Selbige Telekom, die den Netzausbau hier seit Jahren völlig ignoriert hat, kam plötzlich in die Puschen, als drei Alternativanbieter hier auf den Markt gingen. Ab Januar gibt’s VDSL, direkt vor der Haustür.
  8. Ich kann über sexistische und rassistische Witze im öffentlich-rechtlichen Fernsehen nicht lachen.
  9. XMPP fetzt, aber es sollte mal jemand ordentliche Clients dafür bauen.
  10. Ich habe keinerlei Geduld mehr mit Religion, Homöopathie, und sonstige Ignoranz. Fuck Respect. Diese Ignoranz hält uns als Gesellschaft zurück und muss bei jeder Gelegenheit bekämpft werden.
  11. Ich brauche eine BahnCard 100.

Barcamp Hamburg 2010

Am Freitag und Samstag findet das Barcamp Hamburg in den Räumlichkeiten von OTTO statt. Für diejenigen, die das Konzept Barcamp noch nicht kennen, sei der WIkipedia-Artikel “Barcamp” sowie der Artikel “Was ist eigentlich Barcamp?” von Franz Patzig empfohlen.

Das Barcamp Hamburg war in den letzten Jahren meiner Meinung nach immer eines der besten Barcamps des Jahres. Gut organisiert, gute Räumlichkeiten, großartige Leute, und so ziemlich die einzige größere Veranstaltung, wo das WLAN konstant ordentlich funktionierte (Danke an n@work!). Deshalb sind meine Erwartungen auch dieses Jahr wieder hoch, und ich bin sicher, sie werden nicht enttäuscht werden.

Die Sessions beginnen an beiden Tagen um 10:30, vorher gibt es Frühstück und die Sessionplanung. Ich werde wohl eine oder mehrere Sessions zu Google APIs und Events machen, wenn ich die Slides noch ein wenig aufgehübscht kriege.

Android Developer Labs Berlin 2010

Am Dienstag, dem 2. November 2010, fand in Berlin das Google Android Developers Lab in den Räumen der Beuth-Hochschule statt. Geladen wurde kaum mehr als eine Woche vor dem Event, mit teils großen Ankündigungen wie “Android 3.0 Dev Lab” auf XING. Natürlich meldete ich mich als Entwickler mobiler Webseiten und Applikationen sofort an, wie auch viele meiner Freunde.

Zu nachtschlafender Zeit ging es also mit dem ICE nach Berlin, um rechtzeitig um 9 Uhr in Berlin-Wedding anzukommen. Leider landete ich durch eine Kombination aus Zugverspätungen und Verwirrung erst im Zug nach Stuttgart, so dass ich dann – nach Umsteigen in Hannover – doch erst gegen Mittag in Berlin ankam. Aber das sollte nur passende Einstimmung für den Tag sein.

An der Hochschule angekommen war der Wegweiser schnell erspäht, Gebäude und Raum schnell gefunden. Der Zugang zum WLAN dauerte dafür dann um so länger – offenbar war die Authentifizierungs-Infrastruktur überlastet, und auch ein hinzugerufener Techniker der TU konnte nichts weiter tun, als allen zu versichern, dass es wohl funktionieren solle. In der Mittagspause fing sich dann auch die Authentifizierung.

Gerade rechtzeitig, denn ohne Internet wäre es vermutlich zu Ausschreitungen oder Einschlafen gekommen: An Stelle des mehr oder minder offen angekündigten Android 3.0 “Gingerbread” wurden ältere Themen aufgewärmt, teilweise ältere Talks recyclet. Auf Nachfragen des Publikums stritten die Googler jede Kenntnis von “Gingerbread” ab – zumindest auf der Bühne. Im kleineren Kreis konnte man hören, dass man intern bis zuletzt hoffte, Gingerbread endlich öffentlich vorstellen zu können, das System aber nicht rechtzeitig fertig geworden sei. Statt dessen wurde über Visual Design bei Android-Entwicklung (wichtig, aber meiner Meinung nach an der Zielgruppe vorbei) geredet, es wurde vom NDK erzählt (nichts neues, ein paar kleine Ankündigungen), aber der Großteil der Leute, die ich gesehen habe, nutzten die Zeit statt dessen, um z.B. an ihren Android-Apps zu schrauben, Blinkendroid-Matrixen zu bauen, oder Twitter zu lesen. Auch nicht das Schlimmste, aber ich hatte mir von dem Tag mehr versprochen.

In other news, vor dem Google Headquarter steht seit einigen Tagen der Gingerbread Man. Vielleicht klappt’s also zum Google Developer Day am Dienstag.

Using BIGINT columns in rails migrations

Sometimes, you need to use BIGINT columns in your databases, e.g. when working with Twitter whose IDs are over 32 bits long now, and will be 64 bits long soon.

After some googling which didn’t bring up any good results, a quick look into the adapter code helps us:


# activerecord-3.0.0/lib/active_record/connection_adapters/mysql_adapter.rb
# Maps logical Rails types to MySQL-specific data types.
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
  return super unless type.to_s == 'integer'

  case limit
  when 1; 'tinyint'
  when 2; 'smallint'
  when 3; 'mediumint'
  when nil, 4, 11; 'int(11)'  # compatibility with MySQL default
  when 5..8; 'bigint'
  else raise(ActiveRecordError, "No integer type has byte size #{limit}")
  end
end

So, use integer with :limit => 8 to get a bigint column. If you need a primary key of type bigint, you need to pass :id => false to your create_table call, then define the id column manually:


class CreateDemo < ActiveRecord::Migration
  def self.up
    create_table :demo, :id => false do |t|
      t.integer :id, :limit => 8
    end
  end
end

Fighting frameworks

Rant time!

So, back when I was still doing PHP, the story went like this: “We have this kinda okay framework which is documented okay and there are almost no plugins or contributions, so you’re on your own, and if things don’t work, well, you’re screwed, enjoy digging in the depths of the ‘magic’ code.” As soon as you try to do something the framework creators haven’t thought of, it’s like clubbing wild bears to death with only your bare hands and small sticks, in the mud, while torrential rain is falling on you and the air reeks of sulphur. Boy, am I glad I’m done with PHP.

With Rails, it’s different: “We have this super awesome framework which is pretty well documented [ed: in blog posts], oh, except, the new version [ed: rails 3] isn’t, and we have a gazillion of plugins and stuff, but they’re mostly not documented at all, so you’re best off reading the source, and the plugins might or might not work with your version of rails, or they might eat your soul.” You still end up fighting the framework and the plugins, but at least you’re doing it with rapiers. It’s a very civilized form of combat.

(A vaguely related side note, it’s a damn shame how much Yahoo! sucks. They had this great search engine, owned the market, and then they got the idea that they were a ‘media company’ with ‘producers’ managing ‘properties’ and they turned the suck up to eleven. Nowadays, they have a great bunch of very bright people, yet still manage to turn out mostly products that suck. It is quite astonishing. Also, what is it with them launching products and then just discontinuing them a few months later? Are they trying to be like Google?)

Motorola Milestone / Droid and 802.11n Access Points

At UX Camp Europe 2010, my Motorola Milestone could not connect to the barcamp wifi, a WPA2/PSK WLAN running on a mesh of Enterasys APs. The phone would not even manage to associate to the AP, the debug log (adb logcat) showing the following:

E/wpa_supplicant( 3892): ERROR - \
    wpa_driver_tista_private_send - error sending \
    Wext private IOCTL to STA driver \
    (ioctl_cmd = 8021501,  res = -1, errno = 95)
E/wpa_supplicant( 3892): ERROR - Failed to do tista scan!
W/wpa_supplicant( 3892): Failed to initiate AP scan.

So there was never a visible attempt at connecting in the UI.

After a lot of searching, reading source, asking around and debugging, I managed to solve my problem by modifying the configuration of the WLAN driver module (tiwlan.ini) as follows (requires the Android SDK / adb and root access on your phone, or a adb-root recovery partition):

No warranty if you brick your phone, if you don’t feel confident doing this, have someone with Android expertise do it for you or wait for a fix from Motorola. Or try disabling 802.11n (draft-n) in your AP if you can control it.

  1. Connect your phone via USB, make sure you enable USB debugging in Settings > Applications > Development.
  2. Get the configuration file from your phone:

    adb pull /system/etc/wifi/tiwlan.ini tiwlan.ini

  3. Change HT_Enable=1 to HT_Enable=0 in the file.

    sed -i -e s/HT_Enable=1/HT_Enable=0/ tiwlan.ini

  4. Push the changed file back onto your phone (you can’t directly overwrite the file in /system/etc/wifi/ because adb is not running as root on the phone):

    adb push tiwlan.ini /tmp/tiwlan.ini

  5. Start an adb shell on your phone, get root, make /system writable and move the file to the right location:

    adb shell
    su
    mount -o remount,rw -t yaffs2 /dev/block/mtdblock6 /system
    cp -f /tmp/tiwlan.ini /system/etc/wifi/

  6. Reboot your phone

    reboot

Your phone should now be able to associate with the AP.

People have reported similar problems with APs by other manufacturers (Enterasys, D-Link, some old Linksys WRT), so this might work for you.

Rewriting Favcharts – Part 1

Um in Form zu bleiben, widme ich möglichst jeden Tag 30-90 Minuten Nebenprojekten.

Diese Woche baue ich die favcharts neu. Die momentante Implementation der favcharts ist ein Haufen von “Hacks upon hacks”, weil die Charts aus einem Satz hastig programmierter Scripte und viel Zeitdruck entstanden sind. Es funktioniert irgendwie, aber so richtig super ist es nicht. Ein Rewrite ist schon seit einiger Zeit überfällig, jetzt ist es soweit.

Kurz zu den aktuellen favcharts: Der Code ist in PHP geschrieben, die Daten liegen in MySQL, klassischer LAMP-Stack also. Ein Crawler durchforstet regelmäßig die Favorites aller bekannten Nutzer und packt diese Daten in die Datenbank. Das Webfrontend liest aus der DB, macht Auswertungen, und speichert die Resultate dann zwischen, je nach Ansicht (täglich, monatlich, User-spezifisch, etc.) für ein paar Stunden bis mehrere Monate. Beim nächsten Zugriff wird dann nur der Cache gelesen, die Datenbank muss also nicht nochmal ran.

Soweit die Theorie. In der Praxis treten jedoch gehäuft Probleme auf: Viele Requests treffen den Cache gar nicht, weil z.B. jeder seine eigenen Charts abruft, und der User Cache dann irgendwann voll läuft und verdrängt wird. Oder es kommen Suchmaschinencrawler und fragen uralte Charts ab. Außerdem bieten die favcharts relativ viele Ansichten (verschiedene Zeiträume, verschiedene Gewichtungen, etc.), so dass die Anzahl der Cache-Einträge schnell hoch wird. Die Requests, die nicht vom Cache abgehandelt werden können, tun dann richtig weh: Je nach Scoring-Methode muss über riesige Datenmengen berechnet werden. Das kostet Zeit und I/O. Für die gewichtete Bewertung muss sogar das ganze Resultset zweimal durchlaufen werden. Dank mod_php wird dann für geraume Zeit ein Apache-Prozess blockiert und schluckt mehr und mehr Speicher und CPU, und der Rest der Apaches wird dadurch auch nicht schneller. In Stoßzeiten brauchen Requests so manchmal über zehn Sekunden. Der Crawlingprozess funktioniert dafür hervorragend – neue Favs sind oft schon nach wenigen Minuten erfasst. Das Datenbankzugriffsmuster ist allerdings eher ungesund: Gleichzeitig finden viele Writes (von den Crawlern) und viele lange Reads (vom Webfrontend) statt. Das führt zu Congestion, schlechten Indexen, und generell sehr unpraktischen Zugriffsmustern.

All das soll mit dem Rewrite besser werden. In den folgenden Posts beschreibe ich den Prozess des Rewrites, und mit welchen Ansätzen diese Probleme gelöst und in Zukunft verhindert werden sollen.

Und weil ich schon lange nichts mehr mit Ruby gemacht habe, und Rails nach wie vor “in” ist, wird die neue Version auf Ruby on Rails gebaut. Yee-haw.

Twitter-Status mit XING synchronisieren via Xwitter

Mit Xwitter kann man seinen Twitter-Status selektiv mit XING synchronisieren. Dazu muss man nur auf xwitter.de die Schritte durchlaufen und danach Tweets, die nach XING laufen sollen, mit ‘#xwitter’ taggen. Man benötigt dazu aber noch einen XING Premium-Account.

Interessant daran finde ich, dass sich bei XING endlich was zu bewegen scheint. Vor dem Hintergrund sinkender Neuanmeldungen und sinkender Premium-Mitgliedszahlen, außerdem dem sehr großen Momentum, das LinkedIn hat, ist XING nach Ansicht vieler meiner Bekannten jetzt im Zugzwang. Ein erster, zögerlicher Schritt war damals die Übernahme von SocialMedian durch XING und damit einhergehend die Öffnung für interne OpenSocial-Anwendungen. Den großen Schritt einer offenen API hat man sich aber noch nicht getraut, und mit SocialMedian/OpenSocial ist auch nicht all zu viel passiert – es geht schleppend langsam voran, gerade, wenn man das Innovationstempo im “umliegenden” Web sieht.

Um so erstaunlicher dann, dass Xwitter nur wenige Tage nach der Einführung eines ähnlichen Features beim großen Konkurrenten LinkedIn erscheint, noch viel erstaunlicher aber: bei Xwitter handelt es sich um ein halb-offizielles Projekt des VP Engineering Rails von XING, Andreas Gehret. Und er nutzt dazu eine inoffizielle API namens XWS, und bezeichnet es auf Xwitter öffentlich als ‘unfortunate’, dass diese inoffiziell sei.

Ich bin gespannt, was da noch kommt. Ich hoffe, dass XING nicht in einer knee-jerk reaction Xwitter offline nimmt und Andreas Gehret Ärger bekommt. Statt dessen sollte XING die Gelegenheit nutzen und die internen ‘Change Agents’ mal machen lassen: Einen Teil der API öffnen, OpenSocial für alle zulassen, und einige Features von Premium auf Basic-Mitgliedschaften übertragen. Ansonsten wird der mächtige Konkurrent LinkedIn (oder vielleicht sogar Facebook) in gar nicht allzuferner Zukunft XING verdrängen.

Verbesserungsvorschläge habe ich auch schon: 1. Scheint Xwitter momentan regelmäßig zu pollen, also alle registrierten Nutzer abzuklopfen. Das bedeutet, dass es immer eine Weile dauert, bis der Status übernommen wird, und dass man irgendwann in die Twitter-Limits reinläuft. Ich würde daher auf die Twitter Realtime-API umstellen, und nur bei Protected Accounts pollen; 2. würde ich den Hashtag kürzer machen; und 3. würde ich den Hashtag auf XING-Seite dann aus dem Tweet entfernen.

Update: Xwitter ist jetzt Teil der neuen XING Labs, womit die Datenschutzbedenken ausgeräumt sein sollten. Außerdem ist der Hashtag auf #xng verkürzt worden.