Moose Migrate
Moose’s migration system works like version control for your infrastructure. It automatically detects changes in your code and applies them to your data infrastructure with confidence.
Infrastructure Components
Moose tracks changes across:
- OLAP Tables and Materialized Views
- Streaming Topics
- API Endpoints
- Workflows
How It Works
Moose collects all objects defined in your main file (index.tsmain.py) and automatically generates infrastructure operations to match your code:
interface UserSchema {
id: string;
name: string;
email: string;
}
export const usersTable = new OlapTable<UserSchema>("Users");
export const userEvents = new Stream<UserSchema>("Users");from pydantic import BaseModel
from moose_lib import OlapTable, Stream
class UserSchema(BaseModel):
id: str
name: str
email: str
users_table = OlapTable[UserSchema]("Users")
user_events = Stream[UserSchema]("Users")When you add these objects, Moose automatically creates:
- A ClickHouse table named
Userswith theUserSchema - A Redpanda topic named
Userswith theUserSchema
Development Workflow
When running your code in development mode, Moose will automatically hot-reload migrations to your local infrastructure as you save code changes.
Quick Start
Start your development environment:
moose devThis automatically:
- Recursively watches your
/appdirectory for code changes - Parses objects defined in your main file
- Compares the new objects with the current infrastructure state Moose stores internally
- Generates and applies migrations in real-time based on the differences
- Provides immediate feedback on any errors or warnings
- Updates the internal state of your infrastructure to reflect the new state
Example: Adding a New Table
// Before
export const usersTable = new OlapTable<UserSchema>("Users");
// After (add analytics table)
export const usersTable = new OlapTable<UserSchema>("Users");
export const analyticsTable = new OlapTable<AnalyticsSchema>("Analytics");# Before
users_table = OlapTable[UserSchema]("Users")
# After (add analytics table)
users_table = OlapTable[UserSchema]("Users")
analytics_table = OlapTable[AnalyticsSchema]("Analytics")What happens:
- Moose detects the new
analyticsTableobject - Compares: “No Analytics table exists”
- Generates migration: “Create Analytics table”
- Applies migration automatically
- Updates internal state
In your terminal, you will see a log that shows the new table being created:
⠋ Processing Infrastructure changes from file watcher
+ Table: Analytics Version None - id: String, number: Int64, status: String - - deduplicate: falseExample: Schema Changes
import { Key } from "@514labs/moose-lib";
// After (add age field)
interface UserSchema {
id: Key<string>;
name: string;
email: string;
age: number; // New field
}from moose_lib import Key
# After (add age field)
class UserSchema(BaseModel):
id: Key[str]
name: str
email: str
age: int # New fieldWhat happens:
- Moose detects the new
agefield - Generates migration: “Add age column to Users table”
- Applies migration
- Existing rows get NULL/default values
Production Workflow
Moose supports two deployment patterns: Moose Server and Serverless.
Moose Server Deployments
For deployments with a running Moose server, preview changes before applying:
moose plan --url https://your-production-instance --token <token>Authentication Required
Remote planning requires authentication:
- Generate a token:
moose generate hash-token - Configure your server:
[authentication]
admin_api_key = "your-hashed-token"- Use the token with
--tokenflag
Deployment Flow:
- Develop locally with
moose dev - Test changes in local environment
- Plan against production:
moose plan --url <url> --token <token> - Review changes carefully
- Deploy - Moose applies migrations automatically on startup
Serverless Deployments
For serverless deployments (no Moose server), use the ClickHouse connection directly:
# Step 1: Generate migration files
moose generate migration --clickhouse-url <url> --save
# Step 2: Preview changes in PR
moose plan --clickhouse-url clickhouse://user:pass@host:port/database
# Step 3: Execute migration after merge
moose migrate --clickhouse-url <url>Deployment Flow:
- Develop locally with
moose dev - Generate migration plan:
moose generate migration --clickhouse-url <url> --save - Create PR with
plan.yaml,remote_state.json,local_infra_map.json - PR validation: Run
moose plan --clickhouse-url <url>in CI to preview changes - Review migration files and plan output
- Merge PR
- Execute migration: Run
moose migrate --clickhouse-url <url>in CI/CD
Serverless Configuration
Requires state_config.storage = "clickhouse" in moose.config.toml:
[state_config]
storage = "clickhouse"
[features]
olap = true
data_models_v2 = trueClickHouse Requirements for Serverless
Your ClickHouse instance needs the KeeperMap engine for state storage and migration locking.
✅ ClickHouse Cloud: Works out of the box
✅ moose dev or moose prod: Already configured
⚠️ Self-hosted ClickHouse: See ClickHouse KeeperMap documentation for setup requirements
State Storage Options
Moose migrations require storing infrastructure state and coordinating locks. You can choose between two backends:
ClickHouse State Storage (Default)
Uses the _MOOSE_STATE KeeperMap table. Best for:
- ClickHouse Cloud (works out of the box)
- Self-hosted with ClickHouse Keeper already configured
Redis State Storage Uses Redis for state and locking. Best for:
- Existing Redis infrastructure
- Multi-tenant deployments (isolated by
key_prefix) - When ClickHouse Keeper isn’t available
Configuration:
[state_config]
storage = "redis" # or "clickhouse" (default)Usage with Redis:
# With environment variable (recommended)
export MOOSE_REDIS_CONFIG__URL="redis://host:port"
moose migrate --clickhouse-url clickhouse://...
# Or with CLI flag
moose migrate \
--clickhouse-url clickhouse://... \
--redis-url redis://host:portMooseTip:
The ClickHouse URL is always required, even when using Redis for state storage.
Understanding Plan Output
Moose shows exactly what will change:
+ Table: Analytics Version None - id: String, number: Int64, status: String - - deduplicate: false
+ Table: Users Version None - id: String, name: String, email: String - - deduplicate: falseMigration Types
| Change Type | Infrastructure Impact | Data Impact |
|---|---|---|
| Add new object | New table/stream/API created | No impact |
| Remove object | Table/stream/API dropped | All data lost |
| Add field | New column created | Existing rows get NULL/default |
| Remove field | Column dropped | Data permanently lost |
| Change type | Column altered | Data converted if compatible |
Viewing Infrastructure State
Via CLI
# Check current infrastructure objects
moose ls
# View migration logs
moose logsVia Direct Connection
Connect to your local infrastructure using details from moose.config.toml:
[features]
olap = true # ClickHouse for analytics
streaming_engine = true # Redpanda for streaming
workflows = false # Temporal for workflows
[clickhouse_config]
host = "localhost"
host_port = 18123
native_port = 9000
db_name = "local"
user = "panda"
password = "pandapass"
[redpanda_config]
broker = "localhost:19092"
message_timeout_ms = 1000
retention_ms = 30000
replication_factor = 1Best Practices
Development
- Use
moose devfor all local development - Monitor plan outputs for warnings
- Test schema changes with sample data
Production
- Always use remote planning before deployments
- Review changes carefully in production plans
- Maintain proper authentication
- Test migrations in staging first
Managing TTL Outside Moose
If you’re managing ClickHouse TTL settings through other tools or want to avoid migration failures from TTL drift, you can configure Moose to ignore TTL changes:
[migration_config]
ignore_operations = ["ModifyTableTtl", "ModifyColumnTtl"]This tells Moose to:
- Skip generating TTL change operations in migration plans
- Ignore TTL differences during drift detection
You’ll still get migrations for all other schema changes (adding tables, modifying columns, etc.), but TTL changes won’t block your deployments.
Troubleshooting
Authentication Errors
- Verify your authentication token
- Generate a new token:
moose generate hash-token - Check server configuration in
moose.config.toml
Migration Issues
- Check
moose logsfor detailed error messages - Verify object definitions in your main file
- Ensure all required fields are properly typed
- Stuck migration lock: If you see “Migration already in progress” but no migration is running, wait 5 minutes for automatic expiry or manually clear it:
DELETE FROM _MOOSE_STATE WHERE key = 'migration_lock';