Unser Artikel zur PostgREST-Kompatibilität beschreibt detailliert, was der Server funktional abdeckt (Filter, Einbettung, RPC, RLS) und was er Ihnen überlässt. Dieser kommt von woanders. Es dokumentiert mit Aurabase-Code und der offiziellen PostgREST-Dokumentation als Quellen, wo und warum PostgREST tatsächlich im großen Maßstab ein Plateau erreicht. Wir reproduzieren hier keine Lastbank, die wir nicht selbst betrieben haben. Unsere Benchmark-Methodik erklärt, warum uns eine isolierte Zahl ohne veröffentlichtes Protokoll nicht zuverlässig erscheint.
Das Wesentliche
- PostgREST selbst ist leichtgewichtig: 50 bis 250 Millicores CPU, 64 bis 128 MB RAM pro Replikat auf dedizierten Aurabase-Instanzen. Der reine HTTP-Durchsatz ist in der Produktion fast nie der limitierende Faktor.
- Die tatsächliche Obergrenze ist das Postgres-Verbindungsbudget:
PGRST_DB_POOL× Replikate. Im Aurabase-Code verifiziert: 20 Verbindungen pro Projekt auf der dedizierten Ebene (10×2), 4 auf der gemeinsamen Ebene (2×2). Dies ist eine bewusste Entscheidung, um mehr Mandanten auf demselbenmax_connectionsunterzubringen. Prefer: count=exacterzwingt teures MVCC-Scannen bei großen Tabellen. PostgREST dokumentiert zwei günstigere Alternativen:count=plannedundcount=estimatedmit ungefähren Gesamtkosten.- Eine Obergrenze von
db-max-rows(standardmäßig 1000 Zeilen bei Aurabase) kürzt eine Antwort, OHNE sie inContent-Rangezu melden (gemessen unter realen Bedingungen, siehe unten). - Nach einer DDL-Migration wird der PostgREST-Schema-Cache asynchron neu geladen. Das Aurabase-Gateway versucht es bis zu 8 Mal erneut (im schlimmsten Fall etwa 3,5 Sekunden insgesamt), bevor es aufgibt, ein Verhalten, das direkt im Code dokumentiert ist.
Was ein PostgREST-Benchmark misst und was nicht
Ein HTTP-Durchsatztest auf PostgREST misst hauptsächlich Postgres, selten PostgREST. Der Server ist eine dünne Übersetzungsschicht vor der Basis. Bei der überwiegenden Mehrheit der realen Lasten wird die Antwortzeit von der ausgeführten SQL-Abfrage dominiert, nicht vom Prozess, der sie generiert hat.
Das PostgREST-Projekt unterhält ein spezielles Repository für dieses Thema, PostgREST/postgrest-benchmark auf GitHub, das Durchsatzschwankungen von Release zu Release verfolgt, anstatt eine isolierte Marketingzahl zu veröffentlichen. Wir haben es hier weder aufgeführt noch erneut veröffentlicht. Die Ergebnisse hängen von der Hardware, der Schaltplangröße und dem getesteten Szenario ab, also genau den Variablen, die für unser eigenes Benchmark-Protokoll dokumentiert werden müssen, bevor eine Zahl angegeben wird.
Unterhalb von PostgREST ist es pgbench, das die Schicht misst, die wirklich zählt: SQL-Transaktionszeit unter gleichzeitiger Last. Dies ist das offizielle PostgreSQL-Benchmark-Tool (postgresql.org/docs/current/pgbench.html, abgerufen am 24. August 2026). Anstatt dieses Protokoll hier zu reproduzieren, dokumentiert dieser Artikel vier konkrete architektonische Einschränkungen von PostgREST in der Produktion, die jeweils im Aurabase-Quellcode oder in der offiziellen Projektdokumentation überprüft werden.
Der tatsächliche Footprint einer PostgREST-Instanz bei Aurabase
Jedes Aurabase-Postgres-Engine-Projekt erhält zwei dedizierte PostgREST-Replikate, die sich zusammen mit seinem Cluster befinden. Das Kubernetes-Manifest, das sie bereitstellt, legt bescheidene Ressourcen fest.
Was diese Replikate wirklich verbrauchen, ist nicht die CPU: Sie sind Verbindungen zum Postgres-Primärserver. Jede PostgREST-Instanz stellt eine direkte Verbindung zur primären Instanz (-rw) her, ohne den für den Mandanten bereitgestellten PgBouncer-Pooler zu durchlaufen. Diese Auswahl wird bereits in unserem Artikel über PostgREST-Kompatibilitätdetailliert beschrieben: Der Schema-Neulademechanismus LISTEN/NOTIFY erfordert eine dauerhafte Verbindung, die mit einem Pooler im Transaktionsmodus nicht kompatibel ist. Was dieser Artikel hinzufügt: Wie viel kostet es tatsächlich, gemessen an den Verbindungen, und wo ist der Höchstwert?
Die Größe dieses Pools pro Replikat (PGRST_DB_POOL) unterscheidet sich bewusst je nach Projektebene, überprüft in k8s_tenant.rs, der Funktion, die das PostgREST-Manifest für jedes Projekt erstellt:
| Lager | PGRST_DB_POOL / Replikat | Repliken | Verbindungen / erwachtes Projekt |
|---|---|---|---|
| Dediziert (Premium, A1) | 10 (PostgREST-Standard) | 2 | 20 |
| Geteilt (Flotte, Free/Pro/Team) | 2 (Aurabase-Standard, gesenkt) | 2 | 4 |
Auf der dedizierten Ebene wird die Einschränkung gelockert: Ein Projekt verfügt über einen eigenen CNPG-Cluster und daher über einen eigenen max_connections, ohne dass Nachbarn übrig bleiben. Auf der gemeinsamen Ebene teilen sich mehrere Projekte derselben Organisation einen einzigen Cluster: Es ist dieser Kontext, der das Verbindungsbudget entscheidend macht, wie im folgenden Abschnitt erläutert wird.
Das Verbindungsbudget entscheidet darüber, wie viele Mandanten gleichzeitig laufen
Auf einem gemeinsam genutzten Postgres-Cluster ist es nicht der HTTP-Durchsatz, der die Anzahl gleichzeitig aktiver Projekte begrenzt. Dies ist die Anzahl der Verbindungen, die ihre PostgREST-Instanzen auf der Primärseite offen halten, im Vergleich zu den verfügbaren max_connections.
Aurabase leitet dieses Budget direkt aus den tatsächlichen Cluster-Grenzwerten ab, eingecheckt in fleet.rs: (max_connections − réserve superuser/CNPG − connexions réservées au pooler) ÷ connexions par projet éveillé, Untergrenze bei 1. Die feste Reserve beträgt 10 Verbindungen (Superuser, CNPG-Instanzmanager, Metrikexporter, Provisioner-Administratorspielraum). Für die gelieferten Fehler (Pool von 2 pro Replikat, 2 Replikate oder 4 Verbindungen pro aktiviertem Projekt) ergibt die Berechnung je nach Größenstufe des Clusters drei verschiedene Budgets.
Quelle: abgeleitet von fleet.rs::derive_wake_budget und wake_budget_for_org_plan, Aurabase-Code, erneut gelesen am 24. August 2026.
Bei diesem Budget handelt es sich nicht um eine Quote eigener Projekte: Eine team-Organisation kann 50 Projekte verwalten, von denen die meisten ruhen. Dies ist eine Obergrenze für Parallelität: die Anzahl der Projekte, die gleichzeitig offene Verbindungen auf der Primärseite halten können. Eine Aktivierung über das Budget hinaus schlägt nicht fehl, sondern wird verschoben, bis ein Geschwisterprojekt wieder in den Ruhezustand wechselt und in derselben Datei eingecheckt wird. Das Thema der Dimensionierung von max_connections selbst wird in unserem Artikel über die -Optimierung von max_connectionsund den dedizierten/mutualisierten Kompromiss als Ganzes in dedizierter vs. gemeinsam genutzter Basisnäher erläutert.
Warum bevorzugen: count=exact verlangsamt eine Abfrage für eine große Tabelle
Die Abfrage einer genauen Gesamtsumme zwingt Postgres dazu, bei jeder Abfrage die sichtbaren Zeilen des gefilterten Ergebnisses zu zählen, ein Kostenaufwand, der mit der Tabelle wächst, und kein kostenloser Vorgang.
PostgreSQL verwaltet standardmäßig keine indizierten Zeilenzähler. Unter MVCC hängt die Sichtbarkeit einer Zeile von der Transaktion ab, die sie liest. Ein exakter COUNT(*) muss daher die Kandidatenzeilen besuchen, anstatt einen vorberechneten Wert zu lesen. Dies ist eine gut dokumentierte strukturelle Einschränkung im Postgres-Ökosystem, einschließlich Analyseanbietern wie ClickHouse, die ihre eigenen ungefähren Zähler mit dem Transaktionsverhalten von Postgres vergleichen.
PostgREST dokumentiert diese drei Zählstrategien nativ (postgrest.org, abgerufen am 24. August 2026). Die exact-Strategie garantiert einen Gesamtbetrag zum Preis des Scans. planned gibt einen nahezu kostenlosen Kostenvoranschlag vom Abfrageplaner zurück. estimated wechselt basierend auf einem Schwellenwert automatisch zwischen den beiden. Die Wahl ist nicht kosmetischer Natur: Eine Paginierung, die count=exact in einer Tabelle mit mehreren Millionen Zeilen erfordert, zahlt sich für diesen Scan auf jeder Seite aus, auch wenn der Benutzer die letzte nie konsultiert.
Das Abschneiden von db-max-rows ist ohne count=exact unsichtbar
Eine Zeilenobergrenze kann eine PostgREST-Antwort ohne Angabe im Text oder in den Headern abschneiden, es sei denn, es wird ausdrücklich eine genaue Gesamtsumme angefordert. Wir haben es unter realen Bedingungen auf einer dedizierten Aurabase-Instanz gemessen, nicht unter Annahmen.
In einer 10-Zeilen-Testtabelle mit PGRST_DB_MAX_ROWS=5rendert PostgREST v12.2.3 genau den gleichen Content-Range-Header für zwei sehr unterschiedliche Situationen:
| Abfrage | Gerenderte Linien | Inhaltsbereich | Meta (Aurabase) |
|---|---|---|---|
| ?limit=50 (ohne Zählung) | 5/10 echt | 0-4/* | {} |
| ?limit=50&count=exakt | 5/10 echt | 0-4/10 | {insgesamt: 10} |
Ohne count=exactist die Antwort von 5 Zeilen nicht von einer Tabelle zu unterscheiden, die eigentlich nur 5 enthalten würde: Content-Range: 0-4/* beschreibt die gerenderten Zeilen, niemals den angewendeten Grenzwert. Die tatsächliche Obergrenze erscheint in diesem Fall nirgendwo, gemessen direkt im PostgREST-Pfad des Aurabase SDK.
Wenn Ihre PostgREST-Bereitstellung einen db-max-rows festlegt (Aurabase ist standardmäßig 1000), kann ein Client, der data.length mit dem angeforderten Grenzwert vergleicht, um eine vollständige Seite zu erkennen, einen Fehler machen. Der Fehler tritt auf, sobald die Serverobergrenze diesen Grenzwert unterschreitet. Das einzig zuverlässige Signal besteht darin, die Anzahl der empfangenen Zeilen mit dem von count=exactzurückgegebenen total zu vergleichen, was den im vorherigen Abschnitt beschriebenen Kostenkompromiss direkt ins Spiel bringt.
Neuladen des Schemacaches nach einer Migration
PostgREST behält das Postgres-Schema beim Start im Speicher. Nach einem DDL (Tabelle erstellen, Spalte hinzufügen) muss dieser Cache neu geladen werden, bevor die neue Route antwortet, und dieses Neuladen erfolgt asynchron.
Ein Schreibvorgang, der in diesem Fenster eintrifft, erhält möglicherweise eine vorübergehende Fehlermeldung 404 (Cache noch nicht auf dem neuesten Stand), obwohl die Tabelle tatsächlich auf der Postgres-Seite vorhanden ist. Das Aurabase-Gateway fängt dies mit einer begrenzten Wiederholungsschleife auf, die in postgrest_proxy.rsverifiziert wird: bis zu 8 Versuche, zunehmender Backoff (250 ms plus 100 ms pro Versuch), im schlimmsten Fall 3,5 Sekunden kumulativ. Dieser Mechanismus betrifft nur Schreibvorgänge, niemals Lesevorgänge.
Das Gateway gibt kein Reload-Signal aus, es wartet nur. Der einzige wirkliche Auslöser ist ein vom Datenbankdienst auf dem DDL-Pfad ausgegebenes pg_notify('pgrst', 'reload schema'). Wenn ein Migrationspfad vergisst, dieses Signal auszugeben, erschöpfen sich die 8 Versuche in einem Cache, der sich nie ändert, ein Risiko, das im Codekommentar dokumentiert und nicht verschleiert wird.
Für eine selbstgehostete PostgREST-Bereitstellung wird die Lektion verallgemeinert. Jeder DDL-Pfad in Ihrer Anwendung sollte das Neuladen über ein NOTIFY- oder ein SIGUSR1-Signal an den Prozess auslösen. Andernfalls führt eine Migration unmittelbar nach der Bereitstellung zu einer p99-Latenzspitze, die als zeitweise auftretende Fehler getarnt wird.
Was die Architektur schneidet, nicht der Rohdurchsatz
Die vier hier dokumentierten Einschränkungen haben eines gemeinsam: Bei einem isolierten HTTP-Durchsatztest werden keine festgestellt, aber alle vier bestimmen, ob eine PostgREST-Bereitstellung auf die Produktion skaliert werden kann.
- Verbindungsbudget: Begrenzt die Anzahl der gleichzeitig aktiven Mandanten in einem gemeinsam genutzten Cluster, unabhängig vom Durchsatz pro Mandant.
- Kosten für exakten COUNT: wächst mit der Tabelle, nicht mit der Last; wird mit
planned/estimatedumgangen. - Stille Kürzung: Eine korrekt konfigurierte Zeilenobergrenze kann immer noch schlecht instrumentiertes Paging unterbrechen.
- Schema-Neuladen: ein Latenzfenster nach jeder Migration, begrenzt, wenn das Neuladesignal gut verkabelt ist, andernfalls unbegrenzt.
Unabhängig davon, ob Sie zwischen selbst gehostetem PostgREST, einer GraphQL-Ebene im Hasura-Stil oder einer benutzerdefinierten API wählen, sind diese vier Achsen ein besserer Vergleichspunkt als eine isolierte req/s-Zahl. Sehen Sie sich unseren Vergleich an: PostgREST vs. Hasura vs. benutzerdefinierte API. Ebenso wichtig ist die Wahl des Poolers, der vor Ihrer Datenbank steht: Unser Vergleich PgBouncer vs. Supavisor vs. PgCat erläutert, warum PostgREST im Transaktionsmodus keinen Pooler durchlaufen kann.
FAQs
Woran Sie sich erinnern sollten
PostgREST bricht allein unter HTTP-Last fast nie zusammen: Dafür ist seine Architektur zu einfach. Was in der Produktion kaputt geht, ist das, was sie umgibt: wie viele Verbindungen ihre Nachbildungen offen halten, wie viel genau die Gesamtkosten sind. Dazu gehört auch, ob eine Kürzung sichtbar bleibt und wie lange das Fenster nach einer Migration anhält.
Diese vier Einschränkungen gelten nicht nur für Aurabase: Sie gelten für jede PostgREST-Bereitstellung, unabhängig davon, ob sie selbst gehostet oder verwaltet wird. Dieser Code zeigt, wie eine mandantenfähige Bereitstellung sie explizit macht, anstatt sie in der Produktion überraschend zurückzulassen.