The 3 Database Strategies & Tradeoffs¶
FastKitty supports three distinct multi-tenancy database strategies, controlled at startup via a single environment variable:
Choosing the right strategy depends on your isolation requirements, operational overhead, and budget:
| Strategy | Isolation Level | Infrastructure Cost | Operational Complexity | Best For |
|---|---|---|---|---|
database |
Strongest — Separate physical/logical DB per tenant | Highest — One connection pool per active tenant | Moderate — Managed via LRU cache, migrations run per DB | Enterprise SaaS, compliance (HIPAA, SOC2), noisy-neighbor avoidance |
schema |
Strong — PostgreSQL schema namespace (search_path) |
Moderate — Shared DB cluster, shared connection pool | Low — Fast provisioning, isolated tables per tenant | Mid-stage SaaS, fast tenant onboarding, multi-brand portals |
row |
Standard — Shared table, ORM-enforced tenant_id filter |
Lowest — Single DB, single schema, single connection pool | Lowest — Standard single-tenant database operations | Early-stage MVPs, B2C tenants, high tenant count with low per-tenant data |
RDBMS Compatibility & Multi-Database Engine Support¶
FastKitty is built on SQLAlchemy 2.0 async, providing broad multi-database support with specific architectural tradeoffs:
| Database | Supported FastKitty Strategies | How Sub-Schemas Work & Compatibility Notes |
|---|---|---|
| PostgreSQL | All (database, schema, row) |
Primary Target. Full native support for all 3 strategies. Includes dynamic search_path schema switching, async pooling via asyncpg, and automated multi-tenant Alembic migrations. |
| MySQL / MariaDB | database, row |
In MySQL, DATABASE and SCHEMA are exact synonyms (CREATE SCHEMA is identical to CREATE DATABASE). There are no sub-schemas inside a MySQL database. Use either database strategy (separate MySQL databases) or row strategy. |
| SQLite | database, row |
Single-file database. Supported for database (separate .db files per tenant) and row (single .db file with tenant_id column). No native sub-schemas. |
| Microsoft SQL Server | database, row |
Full support for database and row strategies. While MSSQL supports schema namespaces (tenant_1.table), it lacks dynamic per-connection search_path switching without user credential changes. |
| Oracle | database, row |
Full support for database and row strategies. In Oracle, a schema is synonymous with a database USER. |
Single Server (Dev) vs. Multi-Cluster (Production)¶
In local development and Docker Compose, all logical databases (tenant_1, tenant_2, fastkitty_shared) and tenant schemas run inside a single PostgreSQL server container (localhost:5432) for convenience and zero-cost local setup.
However, because every tenant's DatabaseConfig independently defines host, port, username, password, and database_name:
- In database strategy: Tenants can be distributed across completely separate physical or cloud RDS clusters in different AWS/GCP regions (e.g. Tenant 1 on eu-west-1.rds.amazonaws.com and Tenant 2 on us-east-1.rds.amazonaws.com).
- In schema strategy: Tenants share a database cluster, isolated by schema namespaces.
- In row strategy: Tenants share a single database and schema with row-level tenant filtering.
Guarantees & Known Limitations¶
Strategy Guarantees¶
databasestrategy: Strongest isolation. Tenant queries run against distinct physical or logical database instances. Cross-tenant leakage via application bugs is structurally impossible at the database engine level.schemastrategy: Strong isolation via PostgreSQL schema namespaces. Thesearch_pathis set per connection checkout and safely reset on checkin.rowstrategy: FastKitty automatically injects tenant filters on ORM queries viawith_loader_criteriaand stampstenant_idon new models.
Known Limitations & Trade-offs¶
- Raw SQL ORM Bypass (
rowstrategy):
When runningTENANCY_DB_STRATEGY=row, SQLAlchemy'swith_loader_criteriaintercepts only ORM-level queries (select(Model),session.scalars(...)). If a developer executes raw SQL (session.execute(text("SELECT * FROM blog_posts"))), the ORM criteria hook is bypassed. Inrowstrategy, raw SQL must include manualWHERE tenant_id = :tenant_idchecks. Indatabaseandschemastrategies, raw SQL is still fully tenant-isolated. - Engine Support & DDL Complexity (
schemastrategy):
Not all database engines support schemas (e.g. MySQL and SQLite lack true schema namespaces; in MySQL,SCHEMAis synonymous withDATABASE). Additionally, running schema migrations and DDL upgrades across hundreds of tenant schemas can become operationally complex and slow. - Resource & Infrastructure Costs (
databasestrategy):
Dedicated physical or logical databases incur the highest infrastructure costs and connection pool resource consumption (managed via LRU cache). Tenants can be separate physical/managed database servers OR separate logical databases within the same server.