// CASE STUDY · PLATFORM

How we rebuilt a multi-tenant analytics layer in place — no big-bang rewrite.

A brittle shared schema was leaking performance — and once, nearly a row — between tenants. We moved to per-tenant isolation behind one API, one quiet weekly cutover at a time.

ClientNDA — analytics SaaS
IndustrySaaS · Analytics
EngagementPlatform Re-architecture
Timeline11 weeks
StackJava · Postgres · Kafka
// OUTCOMES
−62%Query p95 across the dashboard endpointsnoisy-neighbour effect gone
11 wksTo migrate every tenant, with zero downtimeweekly cutover cadence
0Cross-tenant leakage after isolationenforced at the data layer
100%Of reads behind the new facade API at finishold path retired safely
// THE SITUATION

One schema, every tenant, all the problems.

Every customer's data lived in one shared schema. As the biggest tenants grew, their queries slowed everyone else down — classic noisy neighbour — and a near-miss where one tenant's filter almost returned another's rows had made data isolation an executive-level worry.

A flag-day rewrite was off the table: the platform ran customers' live dashboards, and there was no maintenance window big enough to be safe.

// THE APPROACH

Strangler pattern, one tenant a week.

We put a facade API in front of the data layer so callers stopped touching the schema directly. Behind it, we built the new per-tenant isolated path, then migrated tenants onto it a handful at a time on a weekly cadence — dual-writing and verifying before flipping reads, with a per-tenant rollback the whole way.

Nobody got a “we're migrating this weekend” email. Each Friday a few more tenants moved over and nothing visibly changed, which was the entire point.

// WHAT WE BUILT

The isolation, the migration, the safety net.

  1. Put a facade API in front of everything.

    Callers moved to a single tenant-scoped API, decoupling them from the storage layout so we could change it underneath them.

  2. Built per-tenant isolation.

    Each tenant got its own isolated schema with tenant-scoped indexes, enforced at the data layer so cross-tenant reads are impossible, not just discouraged.

  3. Dual-wrote and backfilled.

    New writes went to both paths while we backfilled history, with a reconciliation job proving the two matched before any cutover.

  4. Made cutovers boring.

    A migration tool flipped reads per tenant after verification, with an instant rollback if a single check failed.

  5. Retired the old path on a schedule.

    Once every tenant was verified on the new path, we removed the shared-schema reads entirely and handed over the runbook.

// THE OUTCOME

Isolated, faster, and migrated by Friday habit.

Before

Shared, brittle schema

  • · one schema for all tenants
  • · noisy-neighbour slowdowns
  • · isolation by convention
  • · scary, all-at-once changes
  • · p95 climbing with growth
After

Isolated, scoped platform

  • · per-tenant schemas
  • · no cross-tenant impact
  • · isolation enforced in data layer
  • · per-tenant cutover + rollback
  • · p95 down 62%

Query p95 dropped 62%, cross-tenant leakage became structurally impossible, and the full migration landed in eleven weeks with zero downtime. The team kept the cutover tooling and the playbook for the next big architectural move.

Every Friday a few more tenants moved over and nobody noticed — which is exactly what we wanted from a migration.
Director of PlatformAnalytics SaaS · name withheld under NDA
// WHAT WE USED

The stack and the services applied.

Tech stack
JavaSpringPostgresKafkaAWS · RDSFlywayOpenTelemetryGrafanak6Terraform
// LET'S TALK

Sitting on an architecture you're afraid to change?

Tell us what's brittle. We'll map a path that ships in increments, not a rewrite weekend.