Viele Entwickler stehen vor der Herausforderung, ihre Rails-Anwendungen performant und skalierbar zu gestalten. Ein verbreiteter Mythos im Internet ist, dass Rails-Anwendungen nicht skalieren. Tatsächlich lässt sich jedoch durch den Einsatz intelligenter Datenbankstrategien wie Sharding eine hohe Skalierbarkeit erreichen. Docker-Container alleine sorgen für horizontale Skalierung auf der Applikationsebene, doch bei Datenbanken ist die Herausforderung eine andere: Wie teilt man die Datenbank effizient auf, um den Datenverkehr auf mehrere Server zu verteilen? Genau an dieser Stelle kommt PgDog ins Spiel. PgDog ist ein Postgres-Proxy, der SQL-Anfragen analysiert und dank automatischer Erkennung der Sharding-Schlüssel entscheidet, an welchen Datenbank-Shard die jeweilige Anfrage weitergeleitet wird.
Die Technologie wurde an einem echten, stark genutzten Beispiel getestet: der Open-Source-Software Mastodon, die Millionen Nutzer bedient und ebenfalls PostgreSQL als Datenbank nutzt. Hierdurch wird nachvollziehbar, wie Sharding in einer praxisnahen Rails-Anwendung funktioniert. Die erste Herausforderung beim Sharding ist die Wahl des sogenannten Sharding-Schlüssels, also einer Spalte in einer Tabelle, anhand derer man die Daten relativ gleichmäßig auf mehrere Datenbanken verteilen kann. Der optimale Sharding-Schlüssel sollte eine zentrale Rolle im Datenmodell spielen, da viele andere Tabellen auf ihn referenzieren müssen, damit die Sharding-Logik effizient umgesetzt werden kann. Im Fall von Mastodon fiel die Wahl auf die Tabelle „accounts“, da diese von ungefähr 75 weiteren Tabellen referenziert wird, was etwa 36 % der Gesamttabellen entspricht.
Betrachtet man auch transitive Fremdschlüsselbeziehungen, so erhöht sich dieser Anteil sogar auf etwa 60 %, was für eine solide Sharding-Grundlage spricht. Eine wichtige Erkenntnis bei der Analyse war, dass nicht alle Tabellen sich für ein Sharding eignen. Einige sogenannte Metadaten-Tabellen, wie „settings“ oder „terms_of_service“, haben keinen Bezug zum Sharding-Schlüssel, werden aber häufig abgefragt. Um diese effizient zu behandeln, hat PgDog das Konzept der „omnisharded“ Tabellen eingeführt. Dabei handelt es sich um Tabellen, die auf allen Shards mit identischer Datenbasis vorliegen.
Anfragen an diese Tabellen werden mittels Round-Robin-Verfahren auf alle Shards verteilt, sodass sich die Last gleichmäßig verteilt, ohne die eigentliche Sharding-Logik zu stören. Bevor das Sharding produktiv eingeführt wird, erleichtert die sogenannte Dry-Run-Funktion von PgDog das Überprüfen der Routing-Entscheidungen. Dabei werden alle Anfragen analysiert und mit einer Hypothese versehen, welchem Shard die Anfrage zugeordnet würde, ohne die Anfragen tatsächlich umzuleiten. Dies ermöglicht es, die Verteilung der Anfragen zu prüfen und potenzielle Engpässe oder Probleme bei der Auswahl des Sharding-Schlüssels zu erkennen. Die aus den Queries generierten Metriken können mittels Prometheus exportiert und mit Tools wie Datadog visualisiert werden, sodass die Erfolgsmessung und -optimierung datenbasiert erfolgen kann.
Ein kritisches Problem beim Sharding mit User-bezogenen Daten ist die richtige Behandlung von Transaktionen, bei denen nicht sofort alle nötigen Sharding-Informationen in der ersten Query enthalten sind. In Mastodon etwa startete eine Transaktion das Einfügen einer Unterhaltung, ohne direkt den Sharding-Schlüssel preiszugeben. Um solche Fälle abzudecken, wurde ein Mechanismus für manuelles Routing eingeführt, bei dem die Sharding-Information explizit per SQL-Befehl SET im Sitzungskontext gespeichert wird. Auf diese Weise kann PgDog wissen, welcher Shard von nachfolgenden Queries innerhalb der Transaktion verwendet werden soll. Für Rails-Anwendungen wurde dieser Mechanismus elegant mit einer Ruby-Gem namens pgdog umgesetzt.
Statt wie üblich ActiveRecord-Transaktionen zu starten, nimmt PgDog durch einen Wrapper die Steuerung über und fügt automatisch den notwendigen SET-Befehl ein. Das vereinfacht die Integration erheblich und minimiert die Gefahr von Fehlkonfigurationen oder fehlenden Sharding-Schlüsseln in Transaktionen. Das Sharding mit PgDog an einem komplexen Projekt wie Mastodon zeigt, wie sich Datenbank-Skalierbarkeit ohne tiefgreifende Änderungen am Anwendungscode ermöglichen lässt. Die sorgfältige Analyse des Schemas und die Nutzung von PostgreSQL-Fremdschlüsseln als Entscheidungsgrundlage für den Sharding-Schlüssel sind dabei essenziell. Metadaten-Tabellen können durch die omnisharded-Strategie effizient eingebunden werden, um Performance-Einbußen zu minimieren.
Während die ersten Resultate einer Live-Simulation noch keinen 100-prozentigen Erfolg brachten, konnten durch die Anpassung und Einbindung omnisharded Tabellen bereits über 50% der Queries horizontal skaliert werden. Ziel ist es, eine sehr hohe Erfolgsrate von etwa 95% zu erreichen, was einer optimalen Verteilung der Datenbanklast entspricht. Mit zusätzlichen Werkzeugen und kommenden Features, wie z. B. einer serverseitigen Lösung zur Primärschlüsselgenerierung und Triggern zum Schutz vor Cross-Shard-Schreiboperationen, ist die vollständige Skalierung sichtbar am Horizont.
Sharding ist eine komplexe, aber sehr lohnende Technik, um Rails-Anwendungen auch bei wachsender Nutzerzahl performant zu halten. Die Arbeit mit PgDog demonstriert, wie ein solcher Umbau praktikabel realisiert werden kann, ohne den gesamten Anwendungscode umschreiben zu müssen. Unternehmen und Entwickler sollten das Potential erkennen und frühzeitig in entsprechende Infrastruktur investieren, um langfristige Skalierbarkeit zu gewährleisten. Noch ist nicht alle Komplexität gelöst: Zwei-Phasen-Commit für garantierte Datenkonsistenz über mehrere Shards hinweg, Automatisierung der notwendigen Codeanpassungen oder die genaue Überwachung der Query-Kosten sind Themen, die in künftigen Erweiterungen behandelt werden. Doch das Grundgerüst steht bereits und zeigt eindrucksvoll, dass Rails-Apps sehr wohl in großen Produktionsumgebungen und bei Millionen Nutzern funktionieren – bei entsprechender Architektur und intelligentem Sharding.