perf(backend): clustering + pool-config + auth-cache voor ~100 gelijktijdige gebruikers#350
Open
robbertbos wants to merge 2 commits into
Open
perf(backend): clustering + pool-config + auth-cache voor ~100 gelijktijdige gebruikers#350robbertbos wants to merge 2 commits into
robbertbos wants to merge 2 commits into
Conversation
…tijdige gebruikers Schaalt de Fastify-backend naar ~100 gelijktijdige gebruikers en benut de beschikbare CPU, met de mitigaties uit de security/standaarden-audit ingebouwd. - Clustering (node:cluster, WEB_CONCURRENCY, default 1 worker/core; 1 = uit). WEB_CONCURRENCY geclampt op [1,64] tegen een fork-bom; crashende workers herstarten. De rate-limit-max wordt over de workers gedeeld (per-worker in-memory store), zodat de cluster-brede limiet bij benadering gelijk blijft. - Expliciete postgres-pool (DB_POOL_MAX/connect/idle-timeout) uit env, met clamping. README documenteert de workers x DB_POOL_MAX <= max_connections rekensom. - Auth-cache (oidcSub -> intern id) zodat pollende clients niet elke request een users-lookup triggeren. Cachet ALLEEN het id (geen persoonsgegevens), TTL <= token-exp, begrensd + evictie. Tokenvalidatie (sig/issuer/azp/exp) en autorisatie blijven per request: een cache-hit kan dus geen toegang lekken. 383 tests, 100% coverage. Clustering runtime-geverifieerd (2 workers, round-robin).
Contributor
🚀 Preview DeploymentYour changes have been deployed to a preview environment: api: https://pr-350-asses-k2n.rig.prd1.gn2.quattro.rijksapps.nl frontend: https://pr-350-asses-k2n.rig.prd1.gn2.quattro.rijksapps.nl This deployment will be automatically cleaned up when the PR is closed. |
robbertbos
added a commit
that referenced
this pull request
Jun 8, 2026
Vervolg-quick-wins op PR1 (#350), drie onafhankelijke verbeteringen: - JWKS: expliciete timeoutDuration 2500ms (i.p.v. de jose-default 5000ms) plus een best-effort warmUpJwks() bij worker-start, zodat de eerste geauthenticeerde request niet de koude Keycloak-fetch betaalt. Tokenvalidatie ongewijzigd. - @fastify/compress (global) voor grote JSON-responses. - sync-poll: de twee onafhankelijke queries parallel via Promise.all. De atomische save (db.transaction) is hier komen te vervallen: die wordt gedekt door de save-race-fix #353 (transactie + conditional UPDATE), inmiddels gemerged. 100% coverage.
robbertbos
added a commit
that referenced
this pull request
Jun 8, 2026
Vervolg-quick-wins op PR1 (#350), drie onafhankelijke verbeteringen: - JWKS: expliciete timeoutDuration 2500ms (i.p.v. de jose-default 5000ms) plus een best-effort warmUpJwks() bij worker-start, zodat de eerste geauthenticeerde request niet de koude Keycloak-fetch betaalt. Tokenvalidatie ongewijzigd. - @fastify/compress (global) voor grote JSON-responses. - sync-poll: de twee onafhankelijke queries parallel via Promise.all. De atomische save (db.transaction) is hier komen te vervallen: die wordt gedekt door de save-race-fix #353 (transactie + conditional UPDATE), inmiddels gemerged. 100% coverage.
robbertbos
added a commit
that referenced
this pull request
Jun 8, 2026
Vervolg-quick-wins op PR1 (#350), drie onafhankelijke verbeteringen: - JWKS: expliciete timeoutDuration 2500ms (i.p.v. de jose-default 5000ms) plus een best-effort warmUpJwks() bij worker-start, zodat de eerste geauthenticeerde request niet de koude Keycloak-fetch betaalt. Tokenvalidatie ongewijzigd. - @fastify/compress (global) voor grote JSON-responses. - sync-poll: de twee onafhankelijke queries parallel via Promise.all. De atomische save (db.transaction) is hier komen te vervallen: die wordt gedekt door de save-race-fix #353 (transactie + conditional UPDATE), inmiddels gemerged. 100% coverage.
…-cap per user De gedeelde RIG-Postgres (rig-db) capt elke project-DB-user op 20 connecties totaal (CONNECTION LIMIT 20; max_connections 250, reserved 10). De vorige defaults (1 worker per core × pool 10) zouden dat budget onder clustering ruim overschrijden en connecties laten weigeren. - WEB_CONCURRENCY default 1 (clustering opt-in i.p.v. één worker per core). De app is I/O-bound, dus één proces met een gezonde pool is de beste balans. - DB_POOL_MAX default 9, ceiling 20. Een rolling deploy draait kort 2 pods, dus 2 × 1 worker × 9 = 18 — een krappe marge onder de cap van 20. - README: budget pods × WEB_CONCURRENCY × DB_POOL_MAX ≤ 20, plus PgBouncer als route voor echte schaal.
37c7b54 to
88ba803
Compare
robbertbos
added a commit
that referenced
this pull request
Jun 8, 2026
Vervolg-quick-wins op PR1 (#350), drie onafhankelijke verbeteringen: - JWKS: expliciete timeoutDuration 2500ms (i.p.v. de jose-default 5000ms) plus een best-effort warmUpJwks() bij worker-start, zodat de eerste geauthenticeerde request niet de koude Keycloak-fetch betaalt. Tokenvalidatie ongewijzigd. - @fastify/compress (global) voor grote JSON-responses. - sync-poll: de twee onafhankelijke queries parallel via Promise.all. De atomische save (db.transaction) is hier komen te vervallen: die wordt gedekt door de save-race-fix #353 (transactie + conditional UPDATE), inmiddels gemerged. 100% coverage.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Doel
Een collega meldde dat de applicatie >5s traag reageert. Naast de diagnose is de eis: ~100 gelijktijdige gebruikers aankunnen én de DB-laag goed afgestemd hebben. Dit is PR1 ("kern eerst"); PR2 (#351) is gestapeld hierop.
Wijzigingen
DB_POOL_MAX,DB_CONNECT_TIMEOUT,DB_IDLE_TIMEOUT) met input-clamping. DefaultDB_POOL_MAX=15, ceiling 20 — afgestemd op de RIG-Postgres (zie onder).oidcSub → intern id) zodat pollende clients niet elke request eenusers-lookup doen. Cachet alléén het id (geen PII), TTL ≤ token-exp, begrensd + evictie, fail-safe.node:cluster,WEB_CONCURRENCY) — standaard 1 worker (opt-in). De app is I/O-bound (lage CPU-load), dus één proces met een gezonde pool is de beste balans; clustering aanzetten kan viaWEB_CONCURRENCY > 1mét een lagere pool. Rate-limit-max wordt over de workers verdeeld (per-worker in-memory store).DB-connectielimiet (RIG-Postgres) — belangrijk
De gedeelde
rig-dbstaat opmax_connections: 250/reserved_connections: 10, en elke project-DB-user is gecapt op 20 connecties totaal (CONNECTION LIMIT 20). Het budget over álle pods/replica's/workers samen is dus 20. Rekensom (zie README):De tweede commit corrigeert de defaults hierop (de oorspronkelijke
1 worker/core × pool 10zou de cap overschrijden). Voor echte schaal onder deze cap is PgBouncer de route (al voorzien als rig-cluster-future).Security/standaarden-audit
Vooraf getoetst tegen BIO/NeRDS/AVG/OIDC + NL GOV ADR/internet.nl/Logboek (multi-agent). Mitigaties ingebouwd: cache alléén id, TTL ≤ exp, begrensd, fail-safe; tokenvalidatie + autorisatie blijven per request (intrekken werkt direct); env-clamping; rate-limit per worker verdeeld.
Verificatie
tsc --noEmitschoon; geen nieuwe dependencies.Follow-ups
PR2 (#351): JWKS warm-up, compressie, save-transactie, sync
Promise.all. Pre-existing hardening-backlog:azpvsaud+jti-replaycheck, Logboek Dataverwerkingen, ADR/openapi.json-pad, CSPunsafe-inline, pinoredact; PgBouncer.