Dit artikel beantwoordt een specifieke vraag – hoe je een backend-API op een reproduceerbare manier kunt benchmarken – door het protocol te documenteren dat we bij Aurabase zullen toepassen voordat we prestatiecijfers publiceren. Geen resultaten: een methode. Elke meting die al elders op deze site is gepubliceerd (met name op onze pagina Prestaties) en die nog niet op dit protocol vertrouwt, moet tot nader order als niet-geverifieerd worden beschouwd.
De essentie
Tot op heden bestaan er geen prestatieresultaten van Aurabase volgens een gepubliceerd, reproduceerbaar protocol - dit artikel documenteert de methodologie die we zullen toepassen om deze te produceren, niet de reeds verkregen resultaten. De repository bevat al een -testsuite met 3 niveaus: Criterion.rs micro-benchmarks op 3 kratten, k6-belastingstests op 8 HTTP/WebSocket-scenario's en een Python-script voor directe Postgres versus API-vergelijking met percentielberekening. Het volledige protocol – meetduur, percentielen in plaats van gemiddelden, omgevingsisolatie, vrijgave van versie en datum – is gebaseerd op geverifieerde externe bronnen: PostgreSQL, Criterion.rs, k6 (Grafana), HdrHistogram, PlanetScale en Convex. Eventuele prestatieclaims die al elders op deze site zijn gepubliceerd zonder dat ze naar dit protocol zijn te herleiden, moeten als niet-geverifieerd worden beschouwd.
Waarom we geen naakte cijfers publiceren
Convex, uitgever van een concurrerende responsieve backend, heeft publiekelijk afstand genomen van wat zijn technische team de ‘staafdiagramoorlog’ tussen databaseproviders noemt. Zijn formule is direct: “Het is theater schalen, niet schalen” – theater schalen, geen echte schaal (stack.convex.dev/on-competitive-benchmarks, Stack technische blog, geraadpleegd op 23 augustus 2026).
Het centrale argument: een benchmark die twee systemen met verschillende garanties op consistentie, topologie of prijsmodel vergelijkt, test vaak niet hetzelfde, ook al beweert hij dat wel te doen – “de benchmark test eigenlijk niet hetzelfde”. Deze reflex heeft een naam in de branche: benchmarketing, waarbij een cijfer wordt gepubliceerd dat is gekozen vanwege zijn marketingeffect en niet vanwege zijn methodologische nauwkeurigheid.
Ons antwoord is niet om te weigeren te meten; weigeren voor onbepaalde tijd een cijfer te publiceren zou net zo oneerlijk zijn als het publiceren van een ongefundeerd cijfer. Het gaat erom eerst te documenteren hoe we zouden meten, met welke instrumenten en onder welke omstandigheden, voordat we beweren iets te hebben gemeten. Dit is ook wat een nuttige vergelijking (zoals onze Aurabase versus Appwrite-vergelijking, die verifieerbare architecturale verschillen documenteert) onderscheidt van een vergelijking van prestatiecijfers zonder een gemeenschappelijk protocol.
Dit is vooral van belang voor een technische lead of een CTO die in een technische commissie een backend-keuze moet verdedigen: een cijfer dat niet te herleiden is tot een methode overleeft de eerste, enigszins indringende vraag niet. Een gedocumenteerd protocol is zelfverdediging: u kunt het script en de geteste versie laten zien en indien nodig de test opnieuw uitvoeren in het bijzijn van iemand.
Wat maakt de meeste backend-benchmarks misleidend
Er komen systematisch twee valkuilen naar voren: het vergelijken van verschillende topologieën zonder dit te rapporteren, en het meten van de latentie op een manier die precies de pauzes verbergt die voor de gebruiker het belangrijkst zijn.
Wat het eerste punt betreft, documenteert PlanetScale expliciet de hardwarepariteitsbeperking: elke vergeleken omgeving moet draaien op computerbronnen (vCPU, RAM) die gelijk zijn aan of groter zijn dan de referentie-instantie, in dezelfde cloudregio (planetscale.com/benchmarks, “Telescope”-methodologie, geraadpleegd op 23 augustus 2026). Zonder deze discipline zou een latency gap simpelweg een weerspiegeling kunnen zijn van een grotere machine en niet van een snellere architectuur.
Hetzelfde principe is van toepassing op de cachestatus en netwerktopologie. Een instance die net is gestart (koude Postgres-cache, lege verbindingspool, queryplan nog niet in de cache opgeslagen) reageert structureel langzamer dan een instance die al een uur onder stabiele belasting draait. Een query uit dezelfde regio als de database reageert structureel sneller dan een query over meerdere regio's. Twee benchmarks die geen van beide specificeren, zijn eenvoudigweg niet vergelijkbaar, zelfs als ze identieke eenheden weergeven.
Wat het tweede punt betreft, wordt de valstrik gecoördineerde omissie genoemd. HdrHistogram, het referentieproject over latentiemeting gemaakt door Gil Tene, legt het als volgt uit: wanneer een belastinggenerator wacht op de reactie van een verzoek voordat hij de volgende verzendt (closed loop), vermindert een servicepauze automatisch het aantal verzoeken dat tijdens de pauze is verzonden - en dus ook het aantal geregistreerde metingen met hoge latentie (github.com/HdrHistogram/HdrHistogram, geraadpleegd in augustus 23, 2026). Het project geeft een concreet en gekwantificeerd voorbeeld: op een hypothetisch systeem dat de latentie elke 10 ms gedurende 200 seconden bemonstert, is een enkele pauze van 100 seconden in het midden van de test voldoende om, zonder correctie, een histogram te produceren waarin ongeveer 99,99% van de reacties onder de 1 ms lijkt te passen - ook al is de helft van de echte tijd verstreken in deze enkele pauze.
Een closed-loop belastingstest die alleen een verzoek verzendt nadat het vorige antwoord is ontvangen, vertegenwoordigt systematisch lange pauzes. De p99 die het weergeeft, kan beter zijn dan de realiteit die een echte gebruiker ervaart – niet omdat het systeem snel is, maar omdat het meetprotocol "vergat" de vragen te verzenden terwijl het was gepauzeerd.
Waarom het gemiddelde liegt: p50, p95, p99
Een gemiddelde latentie lijkt uitstekend als één op de twintig verzoeken vijf keer zo lang duurt. Dit is precies wat de percentielen onthullen en wat het gemiddelde structureel verbergt.
Mechanisch gezien is er niets mysterieus aan een percentiel: sorteer alle gemeten latenties in oplopende volgorde en neem vervolgens de waarde op de overeenkomstige positie. Van de 1000 gesorteerde zoekopdrachten is p50 de 500e waarde, p95 de 950e, p99 de 990e. Eén enkel abnormaal langzaam verzoek onder de 1000 is voldoende om de p99 in beweging te brengen - het is juist de gevoeligheid voor zeldzame gevallen die hem nuttig maakt, waarbij ditzelfde geïsoleerde verzoek bijna geen effect heeft op het gemiddelde.
Veelzeggend teken: het tekstrapport dat pgbench – de officiële PostgreSQL-benchmarktool – standaard weergeeft, geeft een gemiddelde en een standaardafwijking, geen percentielen (postgresql.org/docs/current/pgbench.html, geraadpleegd op 23 augustus 2026). De officiële documentatie waarschuwt ook: “Geloof nooit een test die maar een paar seconden duurt” – geloof nooit een test die maar een paar seconden duurt, wat zowel van toepassing is op de duur als op de gekozen metriek.
k6, de laadtool die we gebruiken voor niveau 2 van onze suite, lost dit op met drempels uitgedrukt in percentiel: de syntaxis p(95)<500 definieert een pass/fail-criterium — 95% van de verzoeken moet binnen 500 ms reageren — rechtstreeks in de testconfiguratie (grafana.com/docs/k6, geraadpleegd op 23 augustus 2026).
| p50 (mediaan) | De helft van de zoekopdrachten is sneller dan deze waarde | Verbergt de distributiestaart volledig |
|---|---|---|
| p95 | 1 op de 20 zoekopdrachten is langzamer | Gebied waar de eerste ontevreden gebruikers verschijnen |
| p99 | 1 op de 100 zoekopdrachten is langzamer | Het meest gevoelig voor gecoördineerde weglating als het protocol slecht is ontworpen |
De 3 benchmarkniveaus die al aanwezig zijn in onze repository
Het publiceren van een methodologie zonder echte hulpmiddelen zou gewoon een andere vorm van theater zijn. De map benchmarks/ in de Aurabase-repository bevat al een suite met 3 niveaus, qua structuur geïnspireerd door de openbare Supabase-methodologie - de tools bestaan, de gemeten en gedateerde resultaten bestaan nog niet.
Niveau 1 — Microbenchmarks Criterion.rs
Drie kratten van de Cargo-werkruimte hebben speciale CPU-gebonden benchmarks: aura-crypto (Argon2-hash, JWT HS256 - generatie, validatie en handtekening voor PostgREST, AES-GCM-codering), aura-db-adapters (parsingfilters en select in PostgREST-formaat - eq., gte., in.(), ingesloten relaties) en aura-core (JSON-serialisatie, schema_nameresolutie, UUID-validatie).
aura-db-adapters meet specifiek de kosten van het parseren van query's in het PostgREST-formaat: vier gevallen voor filters (simple_4, complex_10, or_group, in_large_50 met 50 waarden) en vier voor select (enkele kolommen, *, één relatie-embed, vijf embeds). Dit is het soort kosten dat onzichtbaar is in een globale belastingstest: een regressie op de analyse van een complex or.(...)-filter zou bijna niets veranderen in de p95 van een weinig gebruikt eindpunt, maar zou meetbaar worden op een eindpunt met veel verkeer - vandaar de interesse om dit te isoleren in een micro-benchmark in plaats van uitsluitend op niveau 2 te vertrouwen.
aura-core hanteert een andere aanpak: in plaats van de ruwe tijd te meten, meet het de doorvoer van (Throughput::Bytes) op de JSON-serialisatie en deserialisatie van interne NatsRequest/NatsResponse berichten die worden uitgewisseld tussen de gateway en de services - met drie realistische payload-groottes (een minimaal verzoek, een verzoek met een geneste JSON-body, een lijstantwoord van 50 regels).
Criterion.rs timet niet alleen een lus. Het voert eerst een opwarmfase uit om de CPU/OS-caches te vullen, detecteert uitschieters met een aangepaste versie van de Tukey-methode (zonder ze uit te sluiten van de dataset), berekent betrouwbaarheidsintervallen door een groot aantal opnieuw bemonsterde monsters op te starten, en detecteert prestatieregressies tussen twee runs door de statistische test van Student, met een configureerbare ruisdrempel – doorgaans ±1% – om variaties te negeren die niet statistisch significant zijn (bheisler.github.io/criterion.rs/book/analysis.html, geraadpleegd op 23 augustus 2026).
Elke Criterion-run genereert een gedetailleerd HTML-rapport in target/criterion/: verdelingen, regressiegrafieken, vergelijking met de vorige run. Het is deze relatie, en niet slechts een eindpunt, die een serieuze methodologie het mogelijk moet maken om te regenereren.
Niveau 2 — k6 belastingtesten
Acht k6-scripts bestrijken de -gateway op het datavlak: health (latentiebasislijn), auth-flow (registreren → inloggen → vernieuwen → uitloggen), crud-read en crud-write, storage (uploaden/downloaden), realtime-ws, breakpoint (belastingstoename tot falen) en supabase-compare. Zeven zijn verbonden met een speciaal Makefile-doel — supabase-compare.js bestaat in de repository maar heeft nog geen doel, een stand van zaken die dit artikel documenteert zoals het is in plaats van het te verhullen.
Gedeelde configuratie definieert drempels per bewerkingstype. Dit zijn slaag-/mislukkingscriteria die de test elke keer controleert als deze wordt uitgevoerd, en niet de resultaten die al zijn gemeten:
| Lezen (KRIJGEN) | p95 < 500 ms · p99 < 1000 ms | Configuratie k6 (benchmarks/k6/lib/config.js) |
|---|---|---|
| Schrijven (POST/PATCH) | p95 < 300 ms · p99 < 1000 ms | Configuratie k6 |
| Auth (inloggen/vernieuwen) | p95 < 300 ms · p99 < 1000 ms | Configuratie k6 |
| Opslag (uploaden/downloaden) | p95 < 500 ms · p99 < 2000 ms | Configuratie k6 |
| Foutenpercentage, alle scenario's | < 1 % | Configuratie k6 |
De README.md in de map benchmarks/ documenteert een leesdrempel van p95 bij < 200ms ("Supabase SLO"), terwijl de drempel die feitelijk wordt toegepast in benchmarks/k6/lib/config.js — degene die de test uitvoert — p(95)<500is. De twee bestanden zijn van elkaar afgeleid. Dit is een concreet voorbeeld, gevonden tijdens het lezen van de broncode voor dit artikel, van waarom een protocol één enkele versie van de waarheidsbron zou moeten hebben in plaats van op twee plaatsen te worden gedocumenteerd: zonder deze bron zal zelfs een team dat rigoureus probeert te zijn uiteindelijk tegenstrijdige drempels publiceren.
Niveau 3 — Directe vergelijking tussen PostgreSQL en API
Een Python-script (direct_vs_api.py) meet de werkelijke overhead van de gateway- en servicelaag door directe psycopg2-verzoeken te vergelijken met HTTP-aanroepen in dezelfde bewerking: lijst, eenmalig lezen op id, gefilterd en gesorteerd lezen. Elke meting volgt op een opwarming van 10 iteraties vóór de getimede lus, en berekent vervolgens het gemiddelde, p50, p95, p99 en een doorvoer in bewerkingen per seconde.
Een tweede script (aurabase_vs_supabase.py) past dezelfde opwarm- en percentielberekeningslogica toe op een onderlinge vergelijking met een lokale Supabase-instantie (standaard Supabase CLI, localhost:54321) - dezelfde machine, hetzelfde lokale netwerk voor beide, precies de omgevingspariteitsdiscipline die PlanetScale documenteert voor zijn eigen vergelijkingen.
Een orkestratiescript (collect_baseline.sh, doel bench-baseline van Makefile) verbindt de drie niveaus - Criterium op de 3 kratten, een subset van de k6-scenario's (health en crud-read vandaag, nog niet alle 8), en vervolgens de Python-vergelijking - en schrijft logs, JSON en Criterion HTML-rapporten naar een map met een unieke tijdstempel: benchmarks/results/AAAAMMJJ_HHMMSS/. Dit is precies de reflex van gedateerde openbaarmaking, in één enkele reproduceerbare run, die in de volgende sectie wordt geformaliseerd in een compleet protocol.
Het protocol dat we zullen toepassen voordat we een figuur publiceren
Acht toezeggingen, elk verankerd in een praktijk die al gedocumenteerd is door een erkende tool of project van een derde partij – niet voor deze gelegenheid uitgevonden.
- Voorverwarmen los van meting. Criterion.rs vult CPU/OS-caches vóór de timing;
pgbenchraadt expliciet aan om nooit te geloven in een run van slechts een paar seconden. - Vaste duur, geen vast aantal iteraties. Een belasting heeft tijd nodig om te convergeren — dit is de rol van
stagesk6 en de-Tvlag vanpgbench. - Percentielen, nooit alleen het gemiddelde — en actieve waakzaamheid bij gecoördineerde omissie als de belastingsgenerator in een gesloten lus werkt.
- Omgeving gedetailleerd gedocumenteerd: git commit van de geteste service, versie van PostgreSQL, hardwarespecificatie, versie van de laadtool. PlanetScale documenteert de exacte TPCC-parameters (
TABLES=20,SCALE=250, ~500 GB) om precies deze reden: zonder deze details kan niemand een run reproduceren. - Resultaten met tijdstempel en versienummer, nooit een enkel nummer gegraveerd op een marketingpagina zonder datum. De huidige tooling is al in een gedateerd bestand geschreven; het zal nodig zijn om deze reflex uit te breiden naar elke publiekelijk gepubliceerde meting, waarbij de ontvangende regio wordt gedocumenteerd zoals elke andere omgevingsvariabele (zie onze gids over EU-hostende soevereiniteit, relevant zodra een cijfer afhangt van een bepaalde regio).
- Scripts en onbewerkte gegevens worden gepubliceerd naast het totale resultaat, niet slechts een eindgemiddelde. PlanetScale nodigt lezers zelfs uit om op een speciaal adres een methodologische fout te melden – een houding die we gezond vinden en die we graag willen hervatten.
- Geadverteerde doorvoer naast latentie, niet alleen het een of het ander. Een systeem kan een uitstekende latentie hebben bij lage belasting en de doorvoer instorten naarmate de gelijktijdigheid toeneemt. Dat is precies wat het
breakpoint-scenario van onze k6-suite (scale-to-crash) wil onthullen, en wat Criterion'sThroughput::Bytesmicrobenchmark-meting op functieniveau vastlegt. - Aanzienlijke kloof voordat een verbetering wordt aangekondigd. Een variatie van een paar procent tussen twee runs kan meetruis zijn in plaats van een echte winst. Criterion.rs berekent de waarschijnlijkheid dat het waargenomen verschil te wijten is aan toeval voordat het wordt gekwalificeerd als regressie of verbetering. Een op zichzelf staand cijfer, zonder deze verificatie, is slechts een statistische anekdote.
Wat we niet zullen doen
Deze lijst telt evenveel als het positieve protocol hierboven.
- Het vergelijken van verschillende topologieën (zelf-gehost versus beheerd, koud versus voorverwarmd exemplaar) zonder dit expliciet te rapporteren.
- Behoud de beste serie van de tien zonder de andere negen te vermelden.
- Publiceer een figuur zonder datum, zonder serviceversie, zonder reproductiescript.
- Een bestaand marketingcijfer opnieuw publiceren, zolang het niet terug te voeren is op dit protocol.
- Onszelf vergelijken met een concurrent op basis van een ruw prestatiecijfer als die concurrent zijn eigen methodologie niet op een gelijkwaardige manier publiceert – een cijfer versus stilte is geen vergelijking, het is een slogan.
Een cijfer als “koude start minder dan 1 ms” werd verspreid zonder te worden ondersteund door een reproduceerbare benchmark. Het wordt nu intern behandeld als niet-ondersteund en mag niet worden gelezen als een gemeten kenmerk van het product totdat een gedateerde meting, met gepubliceerde methodologie, dit bevestigt. Dit is precies het soort bewering dat dit protocol bestaat om herhaling te voorkomen.
Het minimale protocol voor het benchmarken van elke backend
Dit protocol is niet afhankelijk van een specifieke Aurabase-tool; u kunt het vandaag nog op uw eigen API toepassen.
- Stel de belasting in vóór de tool: alleen-lezen, schrijven, realistische mix voor uw toepassing - niet een generieke verhouding gekopieerd van een ander project.
- Scheid de voorverwarmingsfase expliciet van de meetfase.
- Voer de test lang genoeg uit: minuten, geen seconden.
- Meet in percentielen (p50/p95/p99), nooit gemiddeld alleen.
- Controleer of uw belastingsgenerator zich niet in een gesloten lus bevindt, of corrigeer de coördinatieomissie in de analyse.
- Isoleer de te testen omgeving: geen luidruchtige buren, geen concurrerende achtergrondtaken.
- Publiceer de geteste versie, datum, hardwarespecificaties en script, niet alleen het eindresultaat.
Op een kale Postgres-basis heeft dit protocol één opdracht pgbench nodig: 20 gelijktijdige clients verdeeld over 4 threads, gedurende 5 minuten, met elke 10 seconden een voortgangsrapport:
Referentietools, per niveau
Vijf tools, elk geschikt voor een ander niveau van de stapel; geen enkele vervangt de andere.
| Microfoon (functie) | Criterium.rs | Pure CPU, bootstrap-statistieken |
|---|---|---|
| SQL-query | pgbbank | TPC-B-achtige transactie, tps en latentie |
| HTTP/WS-belasting | k6 (Grafana) | Percentielen, drempels voor slagen/mislukken |
| OLTP op schaal | sysbench + TPCC (Telescope-methodologie) | QPS, kosten per prestatie |
| Maatcorrectie | HdrHistogram | Compenseert gecoördineerde omissie |