Skip to content

ops: data residency configuration for multi-region deployments#390

Merged
0xVida merged 2 commits intoStellar-Fluid:mainfrom
daveades:fix/data-residency-region-isolation
Mar 30, 2026
Merged

ops: data residency configuration for multi-region deployments#390
0xVida merged 2 commits intoStellar-Fluid:mainfrom
daveades:fix/data-residency-region-isolation

Conversation

@daveades
Copy link
Copy Markdown
Contributor

What

Support deploying Fluid with separate databases per region to comply with data residency requirements (GDPR EU isolation).

How it works

Region selection per instance

Set DATABASE_REGION=EU (or US) on each server instance. Every response carries an X-Fluid-Region header so load balancers and operators can verify routing.

Per-region connection strings

DATABASE_URL_EU=postgresql://...eu-host.../fluid
DATABASE_URL_US=postgresql://...us-host.../fluid

If a regional URL is not set, DATABASE_URL is used as fallback, so single-region deployments require no changes.

Regional DB pool (regionRouter service)

Lazily creates one Prisma client per configured region and caches it for the process lifetime. getDbForRegion(region) always returns the correct client.

Cross-region key bootstrap

findApiKeyAcrossRegions() searches all configured regional DBs in parallel. This solves the bootstrap problem: on the very first request we find the API key regardless of which regional DB it lives in. The resolved region is then cached in Redis so subsequent requests route directly.

Request-level DB routing (apiKeyMiddleware)

After resolving an API key, the middleware attaches res.locals.db (the tenant's regional Prisma client) so all downstream handlers write data to the correct region without any changes to handler code.

Schema

Tenant gains a region field (default "US") with an index. The migration is a non-destructive ALTER TABLE … ADD COLUMN safe to run per-region independently.

Per-region migration

# EU region
DATABASE_URL=$DATABASE_URL_EU npx prisma migrate deploy

# US region
DATABASE_URL=$DATABASE_URL_US npx prisma migrate deploy

Files changed

  • server/src/services/regionRouter.ts — regional DB pool, URL resolution, cross-region key lookup
  • server/prisma/schema.prismaregion field on Tenant
  • server/prisma/migrations/20260329180000_add_tenant_region/ — migration SQL
  • server/src/middleware/apiKeys.ts — attaches res.locals.db and region to ApiKeyConfig
  • server/src/models/tenantStore.tsregion field on Tenant interface
  • server/src/index.ts — calls initializeRegionalDbs() on startup; stamps X-Fluid-Region header
  • server/.env.example — documents DATABASE_REGION, DATABASE_URL_EU, DATABASE_URL_US

Tests

18 unit tests covering:

  • resolveDbUrl: regional URL priority, DATABASE_URL fallback, dev.db fallback
  • DEFAULT_REGION: env var reading and uppercase normalisation
  • getDbForRegion: client pool caching (same instance per region, distinct across regions)
  • isRegionIsolated / getConfiguredRegions: correct region set based on env
  • findApiKeyAcrossRegions: null for missing key, resilience when one regional DB errors, multi-region parallel search

closes #211

@drips-wave
Copy link
Copy Markdown

drips-wave bot commented Mar 29, 2026

@daveades Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@daveades daveades force-pushed the fix/data-residency-region-isolation branch from 90e944f to cfafa82 Compare March 29, 2026 20:04
Add DATABASE_REGION, DATABASE_URL_EU, and DATABASE_URL_US support so
each Fluid instance connects to the correct regional database.

- regionRouter service maintains a lazy Prisma client pool keyed by
  region; getDbForRegion() returns the right client for a given region
- findApiKeyAcrossRegions() searches all configured regional DBs in
  parallel at bootstrap so a key is always found regardless of which
  regional DB it lives in
- apiKeyMiddleware attaches res.locals.db (the tenant's regional client)
  and stores the tenant's region in ApiKeyConfig for downstream use
- Tenant model gains a region field (default "US") with an index
- Every response carries an X-Fluid-Region header for observability
- Tenant and ApiKeyConfig interfaces updated to carry region
- 18 unit tests covering URL resolution, client pooling, region
  detection, and cross-region key lookup resilience

closes Stellar-Fluid#211
@daveades daveades force-pushed the fix/data-residency-region-isolation branch from cfafa82 to 816e856 Compare March 29, 2026 20:09
@0xVida 0xVida merged commit 3e1d0e5 into Stellar-Fluid:main Mar 30, 2026
8 of 12 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 1.21.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Data Residency Configuration (EU/US Region Isolation)

2 participants