Rate-Limiting — Wie Prilog 3.000 Schulen gleichzeitig bedient
Warum das wichtig ist
Prilog ist eine zentrale Plattform: eine einzige API (api.prilog.chat) bedient alle Schulen, Kitas und Einrichtungen gleichzeitig. Jede Einrichtung — ob 15 Eltern oder 200 Lehrkraefte — teilt sich dieselbe Infrastruktur.
Das funktioniert nur, wenn kein einzelner Tenant die Plattform fuer alle anderen lahmlegen kann. Ob durch einen Software-Bug, einen falsch konfigurierten Automatisierungs-Workflow oder einen gezielten Angriff — die anderen 2.999 Schulen muessen davon nichts merken.
Genau das stellt das Rate-Limiting sicher. Es ist kein technisches Detail, sondern eine Verfuegbarkeits-Garantie.
Das Problem ohne Rate-Limiting
Stellen Sie sich vor: Eine Schule hat einen Automatisierungs-Workflow (n8n), der durch einen Konfigurationsfehler in einer Endlosschleife landet. Der Workflow schickt 50.000 API-Requests pro Minute. Ohne Rate-Limiting passiert Folgendes:
- Die API wird ueberlastet — Response-Zeiten steigen von 50ms auf 10 Sekunden
- Alle 3.000 Schulen spueren das: der Chat laed nicht, Krankmeldungen gehen nicht durch, der Morgen-Check haengt
- Bis ein Administrator das Problem bemerkt und manuell eingreift, koennte eine Stunde vergehen
- In dieser Stunde ist die gesamte Plattform fuer 3.000 Einrichtungen unbenutzbar
Mit Rate-Limiting: Der fehlerhafte Workflow wird nach 2.000 Requests (Light-Plan) automatisch gebremst und nach dem Limit blockiert. Die Schule bekommt eine Fehlermeldung. Alle anderen 2.999 Schulen merken nichts.
5-Schichten-Architektur
Prilog schuetzt die Plattform mit fuenf aufeinander aufbauenden Schichten. Jede Schicht faengt eine andere Art von Problem ab. Wenn eine Schicht versagt, greift die naechste.
Internet (Angreifer, Bots, DDoS)
│
┌───────┴─────────────────────────────────────────────┐
│ Schicht 1: Edge-Schutz (Bunny.net) │
│ │
│ Was: Verdaechtige IPs werden blockiert bevor sie │
│ unsere Server ueberhaupt erreichen. │
│ │
│ Schuetzt vor: DDoS-Angriffe, bekannte Bot-Netze, │
│ automatisierte Scans, Geographie-basierte Angriffe │
│ │
│ Wie: DNS-basiertes Filtering, IP-Reputation, │
│ TLS-Terminierung, Challenge-Pages bei Verdacht │
└───────┬─────────────────────────────────────────────┘
│ Nur legitimer Traffic kommt durch
┌───────┴─────────────────────────────────────────────┐
│ Schicht 2: Nginx Rate-Limit (pro Server) │
│ │
│ Was: Grobe IP-basierte Limits, bevor ein Request │
│ ueberhaupt die Anwendung (Node.js) erreicht. │
│ │
│ Schuetzt vor: Einzelne IPs die massiv Requests │
│ senden (Brute-Force Login, API-Scraping) │
│ │
│ Wie: 30 Requests/Sekunde pro IP, Login-Endpoints │
│ strenger (5/Sekunde). Gepuffert, nicht hart. │
└───────┬─────────────────────────────────────────────┘
│ Nur normale Request-Raten kommen durch
┌───────┴─────────────────────────────────────────────┐
│ Schicht 3: Redis Sliding Window (pro Tenant) │
│ │
│ Was: Jede Einrichtung hat ein eigenes Budget. │
│ Alle Mitarbeitenden und Eltern einer Schule │
│ teilen sich dieses Budget. │
│ │
│ Schuetzt vor: Ein Tenant verbraucht │
│ ueberproportional viele Ressourcen. │
│ │
│ Wie: Redis Sorted Sets mit Sliding Window. │
│ Ueberlebt Server-Restarts. Skaliert horizontal. │
│ │
│ Limits nach Plan: │
│ Light (Kita/kleine Schule): 2.000 Req/Minute │
│ Pro (grosse Schule): 10.000 Req/Minute │
│ Dedicated (eigener Server): 20.000 Req/Minute │
└───────┬─────────────────────────────────────────────┘
│
┌───────┴─────────────────────────────────────────────┐
│ Schicht 4: Graceful Degradation │
│ │
│ Was: Statt hart abzuschneiden (0 auf 100 in einem │
│ Request), wird der Tenant sanft abgebremst. │
│ │
│ Schuetzt vor: Schlechte User-Experience. Ein │
│ ploetzliches "429 Too Many Requests" verwirrt. │
│ Stattdessen wird es einfach langsamer. │
│ │
│ Wie: │
│ 0-80% des Budgets: volle Geschwindigkeit │
│ 80-99% des Budgets: kuenstliche Verzoegerung │
│ (linear, max 2 Sekunden) │
│ 100%: HTTP 429 + Retry-After Header │
│ │
│ Der Effekt: Wenn ein Workflow zu schnell laeuft, │
│ wird er automatisch langsamer — oft loest sich das │
│ Problem von selbst, ohne dass jemand eingreifen muss.│
└───────┬─────────────────────────────────────────────┘
│
┌───────┴─────────────────────────────────────────────┐
│ Schicht 5: Monitoring + Alerting │
│ │
│ Was: Echtzeit-Uebersicht welcher Tenant wie viel │
│ verbraucht. Automatische Benachrichtigung bei │
│ Dauerlast. │
│ │
│ Schuetzt vor: Unbemerkte Probleme. Wenn ein Tenant │
│ 10 Minuten lang ueber 80% liegt, stimmt etwas │
│ nicht — der Admin wird per Mail informiert. │
│ │
│ Wie: │
│ - Dashboard: Top-10 Verbraucher, Auslastung in % │
│ - Auto-Alert: Mail bei 80%+ ueber 10 Minuten │
│ - API: GET /admin/rate-limits │
└─────────────────────────────────────────────────────┘Warum 5 Schichten und nicht eine?
Jede Schicht hat einen anderen Blickwinkel:
| Schicht | Sieht | Reagiert auf |
|---|---|---|
| 1. Edge | IP-Adressen | DDoS, Bots, Geographie |
| 2. Nginx | IP + URL | Brute-Force, Scraping |
| 3. Redis | Tenant-Identitaet | Ueberverbrauch, Bug-Loops |
| 4. Degradation | Auslastungs-Level | Sanftes Abbremsen statt Crash |
| 5. Monitoring | Langzeit-Trends | Dauerlast, schleichende Probleme |
Wenn nur eine Schicht existiert (z.B. nur Nginx), dann:
- Ein Tenant mit 200 Usern an 200 verschiedenen IPs umgeht das IP-Limit problemlos
- Ein DDoS mit einer IP wird zwar geblockt, aber erst nachdem er Node.js erreicht hat
- Niemand bemerkt, dass ein Tenant seit Stunden am Limit operiert
Zusammen bilden die 5 Schichten ein lueckenloses Netz.
Fail-Open statt Fail-Closed
Eine wichtige Design-Entscheidung: Wenn Redis ausfaellt (Netzwerk-Problem, Redis-Restart), wird der Request durchgelassen, nicht blockiert.
Warum? Das Rate-Limiting schuetzt vor Missbrauch. Wenn es selbst ausfaellt, ist die Plattform fuer ein paar Minuten ungeschuetzt — das ist besser als die Plattform fuer alle zu blockieren weil der Rate-Limiter kaputt ist.
Das ist ein bewusstes Trade-Off:
- Fail-Closed (Request blockieren wenn Rate-Limiter kaputt): Sicherer, aber ein Redis-Ausfall bedeutet kompletter Plattform-Ausfall
- Fail-Open (Request durchlassen): Weniger sicher fuer wenige Minuten, aber die Plattform laeuft weiter
Fuer eine Schul-Plattform ist Verfuegbarkeit wichtiger als perfekte Rate-Limitierung.
Technische Details
Sliding Window Algorithmus
Anders als ein festes Zeitfenster ("maximal 5000 Requests pro Minute ab :00") nutzt Prilog ein Sliding Window (gleitendes Fenster). Das verhindert die sogenannte "Burst-at-Boundary"-Luecke:
Festes Fenster (schlecht):
Minute 1 (:00-:59): 4999 Requests (knapp unter Limit)
Minute 2 (:00-:59): 5000 Requests
→ In den 2 Sekunden um den Minutenwechsel: 9999 Requests!Sliding Window (gut):
Jeder Request schaut 60 Sekunden zurueck.
Egal wann: es sind nie mehr als 5000 in den letzten 60 Sekunden.Die Implementierung nutzt Redis Sorted Sets:
- Jeder Request wird als Eintrag mit Timestamp gespeichert
- Bei jeder Pruefung werden Eintraege aelter als 60 Sekunden entfernt
- Die Anzahl der verbleibenden Eintraege ist die aktuelle Nutzung
- Alles in einer atomaren Redis-Pipeline (MULTI/EXEC)
Per-Plan Limits
| Plan | Requests/Minute | Typischer Tenant |
|---|---|---|
| Light | 2.000 | Kita, kleine Schule (≤20 User) |
| Pro | 10.000 | Grosse Schule (20-500 User) |
| Dedicated | 20.000 | Eigener Server (unbegrenzt User) |
Die Limits sind grosszuegig bemessen. Zum Vergleich: ein normaler Schulbetrieb mit 200 Usern erzeugt etwa 500-800 Requests pro Minute. Das Pro-Limit von 10.000 bietet also >10x Headroom.
Response-Headers
Jeder API-Response enthaelt Rate-Limit-Informationen:
X-RateLimit-Limit: 10000 ← Maximales Budget
X-RateLimit-Remaining: 9247 ← Verbleibendes Budget
X-RateLimit-Slowdown: 150 ← Kuenstliche Verzoegerung (ms), nur bei >80%
Retry-After: 12 ← Sekunden bis naechster Versuch (nur bei 429)Gut implementierte Clients (Automatisierungen, Integrationen) koennen diese Headers auswerten und ihr Verhalten anpassen.
Was passiert wenn das Limit erreicht wird?
Fuer Endnutzer (Web-Client)
Nichts Sichtbares. Das Web-Client-Budget reicht fuer normalen Betrieb immer aus. Nur wenn eine Automatisierung im Hintergrund laeuft und das Budget aufbraucht, koennte es zu leichten Verzoegerungen kommen (Schicht 4: Graceful Degradation).
Fuer Automatisierungen (n8n)
Workflows die zu schnell laufen, werden automatisch langsamer (Schicht 4). Wenn das Limit erreicht ist, gibt die API HTTP 429 zurueck. Gut geschriebene n8n-Workflows respektieren den Retry-After-Header und warten.
Fuer den Admin
Der Admin sieht im Health-Dashboard:
- Welcher Tenant wie viel verbraucht
- Ob jemand dauerhaft ueber 80% liegt
- Automatische Mail-Benachrichtigung bei Dauerlast
Vergleich mit anderen Plattformen
| Plattform | Rate-Limiting | Ansatz |
|---|---|---|
| Google Workspace | Per-User + Per-Organisation | Sliding Window, 429 + Retry-After |
| Microsoft 365 | Per-Tenant | Fixed Window, 429 + Retry-After |
| Slack API | Per-Token + Per-Workspace | Sliding Window, Tier-basiert |
| Prilog | Per-Tenant + Per-Plan | Sliding Window, Graceful Degradation |
Prilog's Ansatz ist vergleichbar mit dem was Google und Slack nutzen — Sliding Window ist der Industriestandard fuer faire Lastverteilung.
Zusammenfassung
Rate-Limiting ist keine technische Spielerei, sondern eine Grundvoraussetzung fuer den Betrieb einer Multi-Tenant-Plattform. Es garantiert:
- Verfuegbarkeit: Ein Problem bei einer Schule betrifft nie alle anderen
- Fairness: Jede Schule bekommt ihren fairen Anteil an Ressourcen
- Sicherheit: Angriffe werden in 5 Schichten abgefangen
- Transparenz: Admins sehen in Echtzeit wer wie viel verbraucht
- Sanfte Behandlung: Statt harter Ablehnung wird zuerst gebremst
Die Architektur skaliert von 10 bis 10.000 Tenants ohne Aenderung.