Multi-Database¶
Configure and manage multiple databases in a single project.
When to Use Multiple Databases¶
Common scenarios: - Microservices - Each service has its own database - Read/Write Split - Primary for writes, replica for reads - Domain Separation - Transactions, analytics, logs in separate databases - Legacy Integration - New and old databases coexist - Multi-Tenancy - One database per tenant
Basic Setup¶
Configure each database with database_config():
# dbwarden.py
from dbwarden import database_config
# Primary database
database_config(
database_name="primary",
default=True,
database_type="postgresql",
database_url="postgresql://localhost/main",
model_paths=["app.models.primary"],
)
# Analytics database
database_config(
database_name="analytics",
database_type="postgresql",
database_url="postgresql://localhost/analytics",
model_paths=["app.models.analytics"],
)
# Logging database
database_config(
database_name="logging",
database_type="postgresql",
database_url="postgresql://localhost/logs",
model_paths=["app.models.logging"],
)
Model Organization¶
Pattern 1: Separate Modules¶
app/
models/
primary/
__init__.py
user.py
order.py
analytics/
__init__.py
event.py
metric.py
logging/
__init__.py
audit_log.py
Configuration:
database_config(
database_name="primary",
model_paths=["app.models.primary"],
...
)
database_config(
database_name="analytics",
model_paths=["app.models.analytics"],
...
)
database_config(
database_name="logging",
model_paths=["app.models.logging"],
...
)
Pattern 2: Shared Base Classes¶
# app/models/base.py
from sqlalchemy.orm import DeclarativeBase
class PrimaryBase(DeclarativeBase):
pass
class AnalyticsBase(DeclarativeBase):
pass
# app/models/primary/user.py
from app.models.base import PrimaryBase
class User(PrimaryBase):
__tablename__ = "users"
...
# app/models/analytics/event.py
from app.models.base import AnalyticsBase
class Event(AnalyticsBase):
__tablename__ = "events"
...
CLI Usage¶
Target Specific Database¶
# Migrate primary
dbwarden migrate --database primary
# Migrate analytics
dbwarden migrate --database analytics
# Status for logging
dbwarden status --database logging
Target All Databases¶
# Migrate all
dbwarden migrate --all
# Status for all
dbwarden status --all
# Rollback all
dbwarden rollback --all
Default Database¶
The database with default=True is used when --database is omitted:
# These are equivalent when primary is default:
dbwarden migrate
dbwarden migrate --database primary
Migration Directories¶
Each database has its own migration directory:
migrations/
primary/
0001_create_users.sql
0002_create_orders.sql
analytics/
0001_create_events.sql
0002_create_metrics.sql
logging/
0001_create_audit_logs.sql
Configure custom directories:
database_config(
database_name="primary",
migrations_dir="migrations/primary", # Custom path
...
)
Independent Migration Histories¶
Each database maintains its own migration history:
# Check primary history
$ dbwarden history --database primary
Applied Migrations (primary)
0001_create_users (2024-01-15 10:30:00)
0002_create_orders (2024-01-16 11:00:00)
# Check analytics history
$ dbwarden history --database analytics
Applied Migrations (analytics)
0001_create_events (2024-01-15 10:35:00)
Migrations are completely independent - you can migrate one database without affecting others.
Dev Mode with Multiple Databases¶
Configure dev mode for each database:
database_config(
database_name="primary",
default=True,
database_type="postgresql",
database_url="postgresql://localhost/main",
dev_database_type="sqlite",
dev_database_url="sqlite:///./dev_primary.db",
model_paths=["app.models.primary"],
)
database_config(
database_name="analytics",
database_type="postgresql",
database_url="postgresql://localhost/analytics",
dev_database_type="sqlite",
dev_database_url="sqlite:///./dev_analytics.db",
model_paths=["app.models.analytics"],
)
Use dev mode:
# Dev mode for all databases
dbwarden --dev migrate --all
# Dev mode for specific database
dbwarden --dev migrate --database analytics
Common Patterns¶
Pattern 1: Read/Write Split¶
database_config(
database_name="primary",
default=True,
database_type="postgresql",
database_url="postgresql://primary-host/myapp",
model_paths=["app.models"],
)
database_config(
database_name="replica",
database_type="postgresql",
database_url="postgresql://replica-host/myapp",
model_paths=["app.models"], # Same models
overlap_models=True, # Allow overlap
)
Note: Run migrations only against primary; replica replicates automatically.
Pattern 2: Domain Separation¶
# Transactions
database_config(
database_name="transactions",
default=True,
database_type="postgresql",
database_url="postgresql://localhost/transactions",
model_paths=["app.models.transactions"],
)
# Analytics
database_config(
database_name="analytics",
database_type="clickhouse",
database_url="http://localhost:8123/analytics",
model_paths=["app.models.analytics"],
)
# Audit logs
database_config(
database_name="audit",
database_type="postgresql",
database_url="postgresql://localhost/audit",
model_paths=["app.models.audit"],
)
Pattern 3: Multi-Tenant¶
tenants = ["tenant_a", "tenant_b", "tenant_c"]
for tenant in tenants:
database_config(
database_name=tenant,
default=(tenant == "tenant_a"),
database_type="postgresql",
database_url=f"postgresql://localhost/{tenant}",
model_paths=["app.models"], # Same models for all tenants
)
Validation Rules¶
Required: model_paths¶
When you have multiple databases, each must specify model_paths:
# ❌ Error: model_paths required
database_config(database_name="primary", ...)
database_config(database_name="analytics", ...) # Missing model_paths
# ✅ Correct
database_config(
database_name="primary",
model_paths=["app.models.primary"],
...
)
database_config(
database_name="analytics",
model_paths=["app.models.analytics"],
...
)
No Overlap (Default)¶
Model paths cannot overlap:
# ❌ Error: overlap detected
database_config(
database_name="primary",
model_paths=["app.models"],
...
)
database_config(
database_name="analytics",
model_paths=["app.models"], # Same path
...
)
Allow Overlap¶
For read replicas or shared models:
database_config(
database_name="primary",
model_paths=["app.models"],
overlap_models=True, # ← Allow overlap
...
)
database_config(
database_name="replica",
model_paths=["app.models"],
overlap_models=True, # ← Allow overlap
...
)
Troubleshooting¶
"model_paths is required"¶
Solution: Add model_paths to all databases:
database_config(
database_name="primary",
model_paths=["app.models.primary"], # ← Add this
...
)
"model_paths overlap detected"¶
Solution 1: Use separate paths:
model_paths=["app.models.primary"]
model_paths=["app.models.analytics"]
Solution 2: Allow overlap:
overlap_models=True
Wrong database targeted¶
Check default:
dbwarden database # Shows which is default
Be explicit:
dbwarden migrate --database analytics # Specify database
Recap¶
✅ Configure multiple databases with separate database_config() calls
✅ Organize models in separate modules
✅ Each database has independent migration history
✅ Target specific database with --database flag
✅ Target all databases with --all flag
✅ Configure dev mode per database
✅ model_paths required for multi-database
What's Next?¶
- Production Patterns - Deploy multi-database apps
- Troubleshooting - Common issues