Spensara Spensara Knowledge Base & Documentation Repository

Repository Docs & Knowledge Base

Consolidated Spensara documentation repository (722 Markdown & architecture documents in ../repository)
Built Word Documents (.docx): Spensara Overview (.docx) Capability Assessment (.docx) Brink POS Ingestion Analysis (.docx) The Borg Ingestion Architecture (.docx)
etl/docs/features/ETL/tenant-warehouse-schema-evolution.md 6,523 bytes · Updated Oct 23, 2026 10:44 AM EDT
Raw File

Tenant Warehouse Schema Evolution

Problem

Tenant Redshift warehouses are currently initialized through ITenantWarehouseBootstrapper.

This works well for first-time tenant setup, but it does not provide a safe lifecycle for schema evolution.

This is now a concrete issue for the Brink POS ETL flow because staging tables have changed:

  • existing tables were reshaped
  • new staging tables were added
  • more changes are expected later

Today, bootstrap is triggered in two important places:

  • during tenant warehouse setup / debug bootstrap flows
  • when Brink POS credentials are added and ETL provisioning activates the tenant warehouse

The current bootstrap implementation executes embedded SQL artifacts directly against the tenant database, but it does not track schema version or migration history.

Current State In Code

Current bootstrap path:

  • backend/Kds.Ai.Warehouse/Services/TenantWarehouseBootstrapper.cs
  • backend/Kds.Ai.ApiService/Services/CustomerEtlProvisioningService.cs
  • backend/Kds.Ai.ApiService/Jobs/CustomerWarehouseProvisioningJobs.cs
  • backend/Kds.Ai.Cli/Commands/Warehouse/WarehouseCommand.cs

Behavior today:

  • ensure tenant database exists
  • load embedded SQL artifacts by prefix
  • execute bootstrap SQL directly
  • activate tenant mapping

There is no built-in concept of:

  • schema migration history
  • pending vs applied changes
  • forward-only upgrades
  • re-running only the missing changes for already provisioned tenants

Why Bootstrap Alone Is Not Enough

Bootstrap is suitable for:

  • creating a brand new tenant warehouse
  • establishing a baseline schema

Bootstrap is not suitable for ongoing schema evolution because it does not safely handle:

  • adding columns to existing tables
  • creating new staging tables for already active tenants
  • data-preserving structural changes
  • ordered upgrades across multiple releases
  • visibility into which tenant is on which schema version

If schema evolution continues without a migration system, the likely outcomes are:

  • schema drift between tenants
  • fragile SQL scripts that must stay compatible forever
  • accidental destructive changes during reprovisioning
  • uncertainty about whether a tenant database is up to date

Split the concerns into two distinct mechanisms.

1. Bootstrap

Use bootstrap only for first-time tenant creation.

Responsibilities:

  • create tenant database if missing
  • create baseline schemas
  • create baseline tables
  • create migration metadata tables

2. Migrations

Use migrations for every later schema change.

Responsibilities:

  • apply only not-yet-applied changes
  • preserve existing tenant data where possible
  • track applied versions in the tenant warehouse
  • provide deterministic, ordered evolution of staging and curated schemas

Add a tenant-scoped migration runner for Redshift.

Migration history table

Each tenant warehouse should contain a schema migration history table, for example:

  • _meta.schema_migrations

Suggested columns:

  • migration_id
  • migration_name
  • checksum
  • applied_at

This table allows the system to determine which changes were already applied.

Migration file model

Store forward-only SQL migration files as embedded resources, separate from bootstrap.

Example shape:

  • bootstrap resources:
  • baseline only
  • migration resources:
  • ordered incremental SQL files

Examples of future migration files:

  • add snapshot_date to staging tables
  • create staging.brink_settings2_jobs
  • replace old employee staging layout with relational tables

Runtime behavior

For a tenant database:

  1. ensure bootstrap baseline exists
  2. read applied migrations from _meta.schema_migrations
  3. execute unapplied migration files in order
  4. record each successful migration

On tenant provisioning

Provisioning should become:

  1. ensure tenant mapping exists
  2. create tenant database if missing
  3. bootstrap only if the database is new
  4. run migrations
  5. activate tenant mapping

On Brink POS credential add/update

Do not treat credential changes as a reason to re-bootstrap schema.

Instead:

  • ensure provisioning is tracked
  • if tenant warehouse is brand new: bootstrap + migrate
  • if tenant warehouse already exists: migrate only

On local debug startup

Avoid implicit destructive or schema-mutating bootstrap during normal debug application startup.

Prefer:

  • explicit bootstrap command for brand new tenant warehouses
  • explicit migrate command for existing tenant warehouses
  • optional developer-only startup flag if automatic migration is ever needed

API And Operational Entry Points

Existing entry points

Current CLI commands:

  • warehouse setup-tenant-warehouse
  • warehouse bootstrap-tenant
  • warehouse ensure-tenant-warehouse

Current API-side provisioning is indirect through CustomerEtlProvisioningService and background jobs.

Add a dedicated migration runner abstraction, for example:

  • ITenantWarehouseMigrator

Add CLI commands such as:

  • warehouse migrate-tenant <tenantId>
  • warehouse migrate-all-active-tenants

Add an admin-triggerable API only if operationally needed.

Recommended API shape:

  • admin-only
  • asynchronous
  • job-backed
  • idempotent

Example endpoints:

  • POST /admin/customers/{customerId}/warehouse/migrate
  • POST /admin/warehouse/tenants/{tenantId}/migrate

This should trigger a background job, not a long synchronous HTTP request.

Why This Is Better

This approach gives:

  • safe upgrades for already provisioned tenants
  • explicit version tracking
  • repeatable operational behavior
  • clearer support for future staging-schema changes
  • less risk than re-running ever-growing bootstrap SQL

It also matches the current reality that ETL staging schemas will continue to evolve.

  1. Introduce a migration history table in tenant Redshift databases.
  2. Add ITenantWarehouseMigrator and a Redshift implementation.
  3. Keep ITenantWarehouseBootstrapper focused on first-time initialization only.
  4. Update provisioning flow so existing tenants run migrations instead of re-bootstrap.
  5. Add CLI migration commands first.
  6. Add admin API/job entry points if operators need remote triggering.

Suggested Rule Of Thumb

  • New tenant database: bootstrap, then migrate.
  • Existing tenant database: migrate only.
  • Never use bootstrap as the long-term schema evolution mechanism.