---
{
  "title": "Health Samurai Lab: Aidbox Horizontal & Vertical Scaling",
  "description": "Benchmarking von Aidbox mit 8 CPU-Kernen: eine einzelne Instanz im Vergleich zu mehreren. 2×4 verliert nur 6 % RPS, bietet jedoch Redundanz und besseres P99.",
  "date": "2026-03-20",
  "author": "Marat Surmashev",
  "reading-time": "4 min read",
  "tags": ["Infrastructure", "Aidbox", "Health Samurai Lab"]
}
---

8 CPU-Kerne, ein FHIR-Server. Alle Kerne einer einzelnen Aidbox-Instanz zuweisen oder sie auf mehrere aufteilen? Wir haben beide Ansätze einem Benchmark unterzogen — vertikales Scaling (mehr Kerne pro Instanz) und horizontales Scaling (mehr Instanzen mit weniger Kernen).

In unserem [vorherigen Artikel „Game of Pools"](https://www.health-samurai.io/articles/health-samurai-lab-game-of-pools) haben wir gezeigt, dass Aidbox beim Hinzufügen von CPU-Kernen nahezu linear skaliert. Doch lineares vertikales Wachstum ist nur die halbe Geschichte. Was passiert, wenn dieselben Kerne auf mehrere Instanzen verteilt werden?

## Wie Aidbox in einem Cluster funktioniert

Aidbox stützt sich in hohem Maße auf Caches: Validierungs-Engine, Routing, Subscriptions und mehr. Beim horizontalen Scaling besteht die größte Herausforderung darin, diese Caches instanzübergreifend synchron zu halten. Die übliche Lösung ist ein externer Dienst wie Redis.

Aidbox verfolgt einen anderen Ansatz — kein externer Cache erforderlich. Die instanzübergreifende Synchronisierung erfolgt über den PostgreSQL-Mechanismus `LISTEN/NOTIFY`: Wenn eine Instanz Daten aktualisiert, erhalten die anderen eine Benachrichtigung und invalidieren ihren Cache. PostgreSQL, das für den Betrieb ohnehin erforderlich ist, dient gleichzeitig als Event-Bus. Keine zusätzliche Infrastruktur erforderlich.

Wenn Ihre Instanz vollständig statisch ist (d. h. zur Laufzeit werden keine Systemressourcen erstellt), können Sie die Cache-Synchronisierung vollständig deaktivieren. Dies bringt einen geringen Leistungsgewinn, da der `LISTEN/NOTIFY`-Overhead entfällt.

## Testaufbau

Alle Tests liefen auf einer einzigen Maschine. Die Aidbox-Instanzen befinden sich hinter nginx (`least_conn`). Datenbank: eine einzelne PostgreSQL-18-Instanz. Die Last wurde mit k6 erzeugt (5 Minuten, CRUD-Szenario über 9 FHIR-Ressourcentypen). Vor jedem Durchlauf: 30 Sekunden Warmup + 60 Sekunden Cooldown. Die Anzahl der VUs skaliert proportional zur CPU-Kernanzahl (37 VUs pro Kern, 300 VUs für 8 Kerne), um die Latenz über alle Konfigurationen hinweg vergleichbar zu halten.

## Vertikales Scaling: Eine Instanz auf 1, 2, 4, 8 Kernen

Bevor wir Kerne auf mehrere Instanzen aufteilen, legen wir zunächst einen Basiswert fest — wie Aidbox vertikal skaliert, wenn alle Kerne einer einzigen Instanz zugewiesen werden:

![Vertical Scaling](vertical_scaling.svg)

| CPUs | VUs | RPS  | P95 ms | Multiplikator |
| ---- | --- | ---- | ------ | ------------- |
| 1    | 37  | 813  | 88,1   | 1,0 X         |
| 2    | 75  | 1617 | 76,3   | 2,0 X         |
| 4    | 150 | 3176 | 73,3   | 3,9 X         |
| 8    | 300 | 5645 | 78,3   | 7,0 X         |

Eine Verdopplung der CPUs verdoppelt den RPS. Die Latenz bleibt stabil (Durchschnitt 70–90 ms), da die Last proportional zur Kapazität wächst. Bei 8 Kernen beträgt der Multiplikator 7,0× statt dem theoretischen Ideal von 8× — Verluste durch Konkurrenz um gemeinsame Ressourcen (Arbeitsspeicher, GC, PostgreSQL, Netzwerk). Ein solides Ergebnis: Das vertikale Scaling von Aidbox ist vorhersehbar und effizient.

## Horizontales Scaling: Aufteilung von 8 Kernen auf Instanzen

Das zentrale Experiment. Dieselben 8 Kerne werden unterschiedlich verteilt:

![Horizontal Scaling](horizontal_scaling.svg)

| Instanzen | CPUs | RPS  | Avg ms | P95 ms | P99 ms |
| --------- | ---- | ---- | ------ | ------ | ------ |
| 8         | 1    | 3926 | 76,1   | 125,7  | 197,3  |
| 4         | 2    | 5004 | 59,7   | 80,5   | 98,5   |
| 2         | 4    | 5412 | 55,2   | 73,9   | 80,5   |
| 1         | 8    | 5747 | 52,0   | 75,9   | 87,4   |

### Effizienz des horizontalen Scalings

Die Degradation verläuft bis 4×2 allmählich und bei 8×1 sprunghaft:

- 1×8 → 2×4: **−5,8 %** (5747 → 5412)
- 2×4 → 4×2: **−7,5 %** (5412 → 5004)
- 4×2 → 8×1: **−21,5 %** (5004 → 3926)
- 1×8 → 8×1: **−31,7 %** insgesamt

Die Spalte „Effizienz" zeigt, welchen Anteil des theoretischen Maximums (8 × 813 = 6.500 RPS) jede Konfiguration erreicht, sortiert nach Effizienz:

| Strategie  | Konfiguration | RPS  | P99 ms | Effizienz |
| ---------- | ------------- | ---- | ------ | --------- |
| Horizontal | 1×8 CPU       | 5747 | 87,4   | 88,4 %    |
| Horizontal | 2×4 CPU       | 5412 | 80,5   | 83,2 %    |
| Horizontal | 4×2 CPU       | 5004 | 98,5   | 76,9 %    |
| Horizontal | 8×1 CPU       | 3926 | 197,3  | 60,4 %    |

## Arbeitsspeicherverbrauch

Horizontales Scaling hat einen versteckten Kostenpunkt: Jede JVM-Instanz bringt ihren eigenen Arbeitsspeicher-Overhead mit sich. Acht Einzelkern-Instanzen verbrauchen deutlich mehr RAM als eine Acht-Kern-Instanz. Angesichts steigender Hardware-Preise (bedingt durch den KI-Hype) kann dies die Betriebskosten von Aidbox spürbar erhöhen.

![Memory Usage](memory_usage.svg)

| Konfiguration | Mittelwert (GB) | Maximum (GB) |
| ------------- | --------------- | ------------ |
| 8×1 CPU       | 5,5             | 10,4         |
| 4×2 CPU       | 3,0             | 5,6          |
| 2×4 CPU       | 1,6             | 2,6          |
| 1×8 CPU       | 1,1             | 2,1          |

- Der Wechsel von 1×8 auf 2×4 erhöht den Durchschnitt nur um **+0,5 GB**, während 8×1 sowohl im Mittelwert als auch im Spitzenwert ungefähr **5× mehr** Arbeitsspeicher benötigt
- Praktische Schlussfolgerung: 2×4 ist häufig der optimale Kompromiss zwischen Fehlertoleranz und RAM-Budget, während 8×1 nur unter sehr spezifischen Isolationsanforderungen sinnvoll ist

### Zentrale Erkenntnisse

1. **Vertikales Scaling gewinnt beim reinen RPS.** Eine einzelne Instanz auf 8 Kernen holt das Maximum aus der Hardware heraus. Horizontale Aufteilung verursacht stets Overhead.
2. **Horizontales Scaling lohnt sich, wenn Fehlertoleranz gefragt ist.** 2×4 verliert im Vergleich zu 1×8 nur 6 % RPS, bietet jedoch Redundanz und das beste P99 (80,5 ms gegenüber 87–90 ms). Fällt eine Instanz aus, bedient die andere weiterhin die Anfragen.
3. **Der Wendepunkt liegt bei 2 CPUs pro Instanz.** Konfigurationen mit 2+ CPUs (2×4, 4×2) erhalten eine Effizienz von 79–85 %. Unterhalb von 2 CPUs wird der JVM-Overhead kritisch — 8×1 verliert rund 40 % Effizienz und weist das schlechteste P99 aller Konfigurationen auf.
4. **Der Preis der Isolation.** Der Gesamt-RPS einer horizontalen Konfiguration ist bei gleicher Kernanzahl stets niedriger als beim vertikalen Ansatz. Jede JVM bringt ihren eigenen Overhead mit: Cache-Start, GC, Netzwerk, grundlegender Arbeitsspeicherverbrauch. Je kleiner die Instanzen, desto größer der Overhead-Anteil am gesamten Ressourcenbudget.

{% hint style="info" %}
**Unsere Empfehlung:** Teilen Sie nicht zu stark auf. Bei 8 Kernen ist die Konfiguration 2×4 der optimale Kompromiss — Sie erhalten Hochverfügbarkeit mit minimalen Abstrichen: minimaler Leistungsverlust und minimaler Arbeitsspeicher-Overhead. Weniger als 2 CPUs pro Instanz lohnt sich nicht — der JVM-Overhead zehrt das Leistungsbudget schnell auf.
{% endhint %}

## Ausblick

Im nächsten Artikel verlassen wir den Teststand und beobachten, wie sich Aidbox in einer produktionsnahen [Produktionsumgebung](/blog/first-steps-to-get-your-fhir-based-solution-ready-for-production) verhält. Außerdem berechnen wir die tatsächlichen Kosten für den Betrieb von Aidbox in der Cloud.

Siehe auch: [Health Samurai Lab: Game of Pools](/blog/health-samurai-lab-game-of-pools).