Ons artikel over PostgREST-compatibiliteit beschrijft wat de server functioneel dekt (filters, insluiten, RPC, RLS) en wat het aan u overlaat. Deze komt ergens anders vandaan. Het documenteert, met Aurabase-code en de officiële PostgREST-documentatie als bronnen, waar en waarom PostgREST feitelijk op grote schaal een plateau bereikt. We reproduceren hier geen loadbank die we niet zelf hebben beheerd. Onze benchmarkmethodologie legt uit waarom een geïsoleerd cijfer, zonder gepubliceerd protocol, ons niet betrouwbaar lijkt.
De essentie
- PostgREST zelf is lichtgewicht: 50 tot 250 millicores CPU, 64 tot 128 MB RAM per replica op speciale Aurabase-instanties. Raw HTTP-doorvoer is bijna nooit de beperkende factor in de productie.
- Het echte plafond is het Postgres-verbindingsbudget:
PGRST_DB_POOL× replica's. Geverifieerd in de Aurabase-code: 20 verbindingen per project op het speciale niveau (10×2), 4 op het gedeelde niveau (2×2). Dit is een bewuste keuze om meer huurders op dezelfdemax_connectionste plaatsen. Prefer: count=exactforceert dure MVCC-scans op grote tafels. PostgREST documenteert twee goedkopere alternatieven:count=plannedencount=estimated, die een geschat totaal kosten.- Een plafond van
db-max-rows(standaard 1000 regels bij Aurabase) kapt een antwoord af ZONDER het te rapporteren inContent-Range(gemeten in reële omstandigheden, hieronder gedetailleerd). - Na een DDL-migratie wordt de PostgREST-schemacache asynchroon opnieuw geladen. De Aurabase-gateway probeert het maximaal 8 keer opnieuw (ongeveer 3,5 seconden cumulatief in het ergste geval) voordat hij het opgeeft, een gedrag dat rechtstreeks in de code wordt gedocumenteerd.
Wat een PostgREST-benchmark meet en wat niet
Een HTTP-doorvoertest op PostgREST meet voornamelijk Postgres, zelden PostgREST. De server is een dunne vertalingslaag voor de basis. Bij de overgrote meerderheid van de toepassingen in de echte wereld wordt de responstijd gedomineerd door de uitgevoerde SQL-query, en niet door het proces dat deze heeft gegenereerd.
Het PostgREST-project onderhoudt een speciale repository voor dit onderwerp, PostgREST/postgrest-benchmark op GitHub, die doorvoervariaties bijhoudt van release tot release in plaats van een geïsoleerd marketingcijfer te publiceren. We hebben het hier niet uitgevoerd of opnieuw gepubliceerd. De resultaten zijn afhankelijk van de hardware, de schemagrootte en het geteste scenario; precies de variabelen die ons eigen benchmarkprotocol vereist om te worden gedocumenteerd voordat een cijfer wordt geciteerd.
Onder PostgREST staat pgbench, die de laag meet die er echt toe doet: SQL-transactietijd onder gelijktijdige belasting. Dit is de officiële PostgreSQL-benchmarktool (postgresql.org/docs/current/pgbench.html, toegankelijk op 24 augustus 2026). In plaats van dit protocol hier te reproduceren, documenteert dit artikel vier concrete architecturale beperkingen van PostgREST in productie, elk geverifieerd in de Aurabase-broncode of in de officiële projectdocumentatie.
De daadwerkelijke voetafdruk van een PostgREST-instantie bij Aurabase
Elk Aurabase Postgres-engineproject ontvangt twee speciale PostgREST-replica's, op dezelfde locatie als het cluster. Het Kubernetes-manifest dat ze inzet, stelt bescheiden middelen vast.
Wat deze replica's werkelijk verbruiken is niet de CPU: het zijn verbindingen met de primaire Postgres. Elke PostgREST-instantie maakt rechtstreeks verbinding met de primaire (-rw), zonder de PgBouncer-pooler te doorlopen die voor de tenant is geïmplementeerd. Deze keuze wordt al gedetailleerd beschreven in ons artikel over PostgREST-compatibiliteit: het herlaadmechanisme van het LISTEN/NOTIFY schema vereist een permanente verbinding, die niet compatibel is met een pooler in transactiemodus. Wat dit artikel toevoegt: hoeveel kost het eigenlijk, qua aansluitingen, en waar het piekt.
De grootte van deze pool per replica (PGRST_DB_POOL) verschilt opzettelijk afhankelijk van het projectniveau, geverifieerd in k8s_tenant.rs, de functie die het PostgREST-manifest voor elk project samenstelt:
| Lager | PGRST_DB_POOL / replica | Replica's | Verbindingen / ontwaakt project |
|---|---|---|---|
| Speciaal (premium, A1) | 10 (PostgREST-standaard) | 2 | 20 |
| Gedeeld (vloot, gratis/pro/team) | 2 (Aurabase standaard, verlaagd) | 2 | 4 |
Op het specifieke niveau zijn de beperkingen versoepeld: een project heeft zijn eigen CNPG-cluster, dus zijn eigen max_connections, zonder dat er buren over zijn. Op gedeeld niveau delen meerdere projecten van dezelfde organisatie één cluster: het is deze context die het budget voor verbindingen doorslaggevend maakt, zoals uitgewerkt in de volgende paragraaf.
Het aansluitbudget bepaalt hoeveel tenants er tegelijkertijd actief zijn
Op een gedeeld Postgres-cluster is het niet de HTTP-doorvoer die het aantal gelijktijdig actieve projecten beperkt. Dit is het aantal verbindingen dat hun PostgREST-instanties openhouden op de primaire verbinding, vergeleken met de beschikbare max_connections.
Aurabase leidt dit budget rechtstreeks af van de daadwerkelijke clusterlimieten, gecontroleerd in fleet.rs: (max_connections − réserve superuser/CNPG − connexions réservées au pooler) ÷ connexions par projet éveillé, minimum op 1. De vaste reserve is 10 verbindingen (superuser, CNPG instance manager, metrics exporter, provisioner admin marge). Voor de geleverde defecten (pool van 2 per replica, 2 replica's of 4 verbindingen per ontwaakt project) levert de berekening drie verschillende budgetten op, afhankelijk van het grootteniveau van het cluster.
Bron: afgeleid van fleet.rs::derive_wake_budget en wake_budget_for_org_plan, Aurabase-code, herlezen op 24 augustus 2026.
Dit budget is geen quotum voor projecten in eigendom: een team organisatie kan 50 projecten beheren, waarvan de meeste inactief zijn. Dit is een limiet van gelijktijdigheid: het aantal projecten dat tegelijkertijd open verbindingen kan bevatten op de primaire. Een wake-up over budget mislukt niet; het wordt uitgesteld totdat een zusterproject weer in slaap valt, gecontroleerd in hetzelfde bestand. Het onderwerp van de dimensionering van max_connections zelf wordt uitgebreid besproken in ons artikel over afstemming van max_connections, en de dedicated/gemutualiseerde afweging als geheel in dedicated vs. gedeelde basis.
Waarom liever: count=exact vertraagt een zoekopdracht in een grote tabel
Als u om een exact totaal vraagt, moet Postgres bij elke zoekopdracht de zichtbare rijen van het gefilterde resultaat tellen, een kosten die met de tabel meegroeit, en geen gratis bewerking.
PostgreSQL onderhoudt standaard geen geïndexeerde rijtellers. Onder MVCC hangt de zichtbaarheid van een rij af van de transactie die deze leest. Een exacte COUNT(*) moet daarom de kandidaatrijen bezoeken in plaats van een vooraf berekende waarde te lezen. Dit is een goed gedocumenteerde structurele beperking in het Postgres-ecosysteem, inclusief analyseleveranciers als ClickHouse, die hun eigen geschatte tellers vergelijken met het transactiegedrag van Postgres.
PostgREST documenteert deze drie telstrategieën native (postgrest.org, geraadpleegd op 24 augustus 2026). De exact-strategie garandeert een totaal tegen de prijs van de scan. planned retourneert een vrijwel gratis schatting van de queryplanner. estimated schakelt automatisch tussen de twee op basis van een drempelwaarde. De keuze is niet cosmetisch: een paginering die count=exact vereist op een tabel van enkele miljoenen rijen, betaalt voor deze scan op elke pagina, zelfs als de gebruiker de laatste nooit raadpleegt.
Het afkappen van db-max-rijen is onzichtbaar zonder count=exact
Een rijlimiet kan een PostgREST-antwoord afkappen zonder enige indicatie in de hoofdtekst of headers, tenzij expliciet om een exact totaal wordt gevraagd. We hebben het onder reële omstandigheden gemeten op een speciale Aurabase-instantie, en niet aangenomen.
Op een testtabel met 10 rijen met PGRST_DB_MAX_ROWS=5geeft PostgREST v12.2.3 exact dezelfde Content-Range header weer voor twee heel verschillende situaties:
| Vraag | Gesmolten lijnen | Inhoudsbereik | meta (Aurabase) |
|---|---|---|---|
| ?limit=50 (zonder telling) | 5/10 echt | 0-4/* | {} |
| ?limit=50&count=exact | 5/10 echt | 0-4/10 | {totaal: 10} |
Zonder count=exactis het antwoord van 5 rijen niet te onderscheiden van een tabel die er eigenlijk maar 5 zou bevatten: Content-Range: 0-4/* beschrijft de weergegeven rijen, nooit de toegepaste limiet. De werkelijke limiet die van kracht is, verschijnt in dit geval nergens, rechtstreeks gemeten op het PostgREST-pad van de Aurabase SDK.
Als uw PostgREST-implementatie een db-max-rows instelt (Aurabase is standaard ingesteld op 1000), kan een client die data.length vergelijkt met de gevraagde limiet om een volledige pagina te detecteren, zich vergissen. De fout verschijnt zodra het serverplafond lager is dan deze limiet. Het enige betrouwbare signaal is het vergelijken van het aantal ontvangen lijnen met de total die wordt geretourneerd door count=exact, wat direct een rol speelt in de kostenafweging die in de vorige sectie is beschreven.
De schemacache opnieuw laden na een migratie
PostgREST houdt het Postgres-schema bij het opstarten in het geheugen. Na een DDL (tabel maken, kolom toevoegen) moet deze cache opnieuw worden geladen voordat de nieuwe route reageert, en dit herladen is asynchroon.
Een schrijfbewerking die in dit venster binnenkomt, ontvangt mogelijk een tijdelijke 404 (cache nog niet up-to-date), ook al bestaat de tabel inderdaad aan de Postgres-kant. De Aurabase-gateway absorbeert dit met een begrensde herhalingslus, geverifieerd in postgrest_proxy.rs: maximaal 8 pogingen, toenemende backoff (250 ms plus 100 ms per poging), 3,5 seconden cumulatief in het ergste geval. Dit mechanisme heeft alleen invloed op schrijven, nooit op lezen.
De gateway geeft geen herlaadsignaal af: hij wacht alleen maar. De enige echte trigger is een pg_notify('pgrst', 'reload schema') uitgegeven door de databaseservice op het DDL-pad. Als een migratiepad vergeet dit signaal uit te zenden, worden de acht pogingen uitgeput in een cache die nooit zal veranderen, een risico dat wordt gedocumenteerd zoals in het codecommentaar, en niet verhuld.
Voor een zelf-hostende PostgREST-implementatie is de les generaliseerbaar. Elk DDL-pad in uw toepassing moet het opnieuw laden activeren, via NOTIFY of een SIGUSR1-signaal naar het proces. Anders veroorzaakt een migratie direct na de implementatie een p99-latentiepiek, vermomd als periodieke fouten.
Wat de architectuur snijdt, niet de ruwe doorvoer
De vier hier gedocumenteerde beperkingen hebben één ding gemeen: geen enkele wordt gezien bij een geïsoleerde HTTP-doorvoertest, maar ze bepalen alle vier of een PostgREST-implementatie naar productie kan worden geschaald.
- Verbindingsbudget: beperkt het aantal tenants dat tegelijkertijd actief is op een gedeeld cluster, ongeacht de doorvoer per tenant.
- Kosten van exact COUNT: groeit mee met de tafel, niet met de belasting; wordt omzeild met
planned/estimated. - Stille afkapping: een correct geconfigureerde rijkap kan nog steeds slecht geïnstrumenteerde paging verbreken.
- Schema herladen: een latentievenster na elke migratie, begrensd als het herlaadsignaal goed bedraad is, anders onbeperkt.
Of u nu kiest tussen zelf-gehoste PostgREST, een GraphQL-laag in Hasura-stijl of een aangepaste API, deze vier assen zijn een beter vergelijkingspunt dan een geïsoleerd req/s-cijfer. Zie onze vergelijking PostgREST versus Hasura versus aangepaste API. De keuze van de pooler die voor uw database staat, is net zo belangrijk: onze vergelijking PgBouncer versus Supavisor versus PgCat legt uit waarom PostgREST niet door een pooler kan gaan in de transactiemodus.
Veelgestelde vragen
Wat te onthouden
PostgREST gaat vrijwel nooit kapot onder HTTP-belasting alleen: daarvoor is de architectuur te simpel. Wat in de productie kapot gaat, is wat eromheen zit: hoeveel verbindingen de replica's openhouden, hoeveel een exact totaal kost. Hieronder valt ook of een afknotting zichtbaar blijft en hoe lang het venster na een migratie duurt.
Deze vier beperkingen zijn niet specifiek voor Aurabase: ze zijn van toepassing op elke PostgREST-implementatie, zelfgehost of beheerd. Wat deze code laat zien, is hoe een implementatie met meerdere tenants deze expliciet maakt in plaats van ze verrassend achter te laten in de productie.