Kategorie

Rails Server: Performance Optimierung

April 16th, 2012

Performance Optimierung am Rails Server durch Passenger Konfigurationen

Im letzten Artikel habe ich gezeigt wie man eine Applikation auf dem Rails Server installiert. Heute werde ich darauf eingehen wie man die Performance mit dem Rails Server noch weiter steigern kann.

Als Beispiel nehme ich wieder die Applikation vereine.ch.

Stand der Applikation

Es wurden Query-Optimierungen mit DB-Indexes vorgenommen. Zudem profitieren wir vom Rails Caching im production Environment.

Da vereine.ch zuerst auf normalem Internet Standard Hosting betrieben wurde konnten keine Optimierungen an MySQL und Apache/Passenger Konfiguration gemacht werden. Auch auf Result Caching mit memcached mussten wir verzichten.

Messungen mit altem Hosting

Wir haben die meistbesuchten Pages von vereine.ch einem Stresstest unterzogen. Dazu wird jede Page parallel von mehreren Threads abgerufen. Als Resultate erhalten wir die Response Time der Applikation in MS. Wir verwenden hier den 90% Wert um noise auszufiltern.

Diese Resultate erhalte ich mit 5 Threads die je 10 Requests absetzen:

Hier ist eine kurze Erklärung der Abfragen angebracht.

  • Live Search ist eine Abfrage auf Vereine mit Einschränkung auf Kategorie und Umkreis (geo-Daten). Es simuliert einen AJAX Request und lädt somit keine Layoutdaten. Ist auf 15 Resultate beschränkt.
  • Kategorien zeigt mir die 15 Hauptkategorien und die Anzahl dazu eingetragener Vereine und Dienstleistungen an.
  • Index ist ähnlich wie Live Search nur diesmal mit Layout.
  • Kategorien Vereineinfo ist wieder ein AJAX Request der von der grössten Kategorie (mit ca 600 Vereinen) alle Vereine mit Link lädt. In den Resulateten zeigt sich hier eindeutiges Verbesserungspotenzial.
  • More Results ist nochmal ein Live Search Request diesmal einfach mit offset.
  • String Search macht auch eine Abfrage auf 15 Vereine, diesmal wird aber Fulltext Search über ein Textfeld verwendet.

Den Werten ist zu entnehmen, dass Kategorien Vereineinfo viel zu langsam lädt und auch Index mit über einer Sekunde relativ viel Zeit beansprucht. Die Anderen Pages sind mit einer Response Time von 300 bis 700 ms zwar annehmbar aber bestimmt nicht optimal.

Im zweiten Durchlauf erhöhen wir die Anzahl Threads auf 10 und belassen die Anzahl Requests pro Thread auf 10.

Die Resulate sind katastrophal. Die beste Response Time liegt bei 2.7 Sekunden und ist damit weit über dem Grenzwert für ein angenehmes Benutzererlebnis.

Wie lässt sich diese drastische Verschlechterung erklären?

Unser Internet Standard Hosting erlaubt es uns leider nur 2 Passenger Instanzen gleichzeitig laufen zu lassen. Da jede Instanz nur einen Request verarbeiten kann steigt somit die Response Time linear zur Anzahl Benutzer und erreicht somit sehr schnell eine kritische Länge.

Optimierung am Rails Server

Apache und Passenger

Wir wissen jetzt, dass die Anzahl Passenger Instanzen einen starken Einfluss auf die Performance einer Rails Applikation hat. Aber wie verhält sich das genau?

Eine Passenger Instanz ist ein Prozess der eine Kopie meiner Rails Applikation im RAM hält um möglichst schnell einen Request beantworten zu können. Das heisst wie viele Passenger Instanz ich starten kann hängt davon ab wie viel unbenutztes RAM mir auf meinem Server zur Verfügung steht. Wenn ich aber zu viele Instanzen starte fängt der Server an zu swappen was für die Performance noch schädlicher ist als zu wenig Passenger Prozesse zu haben.

Die perfekte Zahl von Instanzen hängt dabei natürlich immer von der Applikation ab. Um diese zu bestimmen sind die Befehel passenger-memory-stats und free -m sehr hilfreich. Wobei der Erste mir sagt wie viel RAM meine Applikation braucht und der Zweite mir sagt wie viel auf dem Server noch ungenutzt ist. Als Faustregel hat es sich bewährt 15 Instanzen pro GB Memory zu konfigurieren.

Um das so für vereine.ch zu Konfigurieren öffne ich das entsprechende Apache Configfile und füge folgendes zur VirtualHost Direktive hinzu:

PassengerMaxPoolSize 15

Hier sind noch ein paar weitere Passenger Optionen die sich für uns bewährt haben.

  # Speeds up spawn time by a lot but some apps might be incompatible
  PassengerSpawnMethod smart
  # ApplicationSpawner is kept alive. 
  # This speeds up spawning of new Application Instances.
  RailsAppSpawnerIdleTime 0
  # Application Instances are kept alive longer
  PassengerPoolIdleTime 1000
  # Perform filesystem checks (i.e. check for restart.txt) only once every 5 seconds. 
  # Default is on every request
  PassengerStatThrottleRate 5
  # Explicitly specify that the host is a Ruby on Rails application.
  RailsAutoDetect off
  RailsBaseURI /

So, wie verhält sich unser Test mit 5 Benutzern à 10 Requests nun mit der neuen Konfiguration?

Man sieht jetzt schon eine klare Verbesserung gegenüber dem Standard Hosting. Die Response Time fast aller Requests ist stark gesunken und zwischen 100 – 400ms schon viel näher am Optimum.

Den massivsten Unterschied sieht man aber wenn man den Test mit 10 Benutzern à 10 Requests durchführt.

Die meisten Requests können hier eine Responsetime von unter einer Sekunde halten. Das ist schon eine massive Verbesserung wenn man bedenkt wie wenig Aufwand dazu nötig war.

Durch diese Resultate wird auch klar, dass der Engpass von Kategorien Vereineinfo in der Datenbank zu suchen sein wird.

Im nächsten Artikel werde ich Rails Server Optimierungen im Bereich Caching behandeln.