Developer: s4gor
Github: https://github.com/s4gor
Production-grade schema synchronization for multi-tenant databases.
Schema Sync is designed to manage schema synchronization across multiple tenant schemas in a single database. It provides a robust, extensible foundation for detecting, analyzing, and managing schema differences safely.
This crate is designed extension-first. All core abstractions are trait-based, allowing:
- Multi-database support: PostgreSQL, MySQL, SQLite, and more
- Pluggable migration engines: SQL files, Rust code, external tools
- Multiple operation modes: Sync, dry-run, validation, audit
- Flexible snapshot storage: Filesystem, database, version control
┌─────────────────────────────────────────────────────────────┐
│ CLI Layer │
│ (dry-run, diff, validation, audit modes) │
└───────────────────────┬─────────────────────────────────────┘
│
┌───────────────────────▼─────────────────────────────────────┐
│ Engine Layer │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Planner │→ │ Executor │→ │ Diff │→ │ Snapshot │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└───────────────────────┬─────────────────────────────────────┘
│
┌───────────────────────▼─────────────────────────────────────┐
│ Adapter Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Database │ │ Migration │ │ Schema │ │
│ │ Adapter │ │ Runner │ │ Inspector │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└───────────────────────┬─────────────────────────────────────┘
│
┌───────────────────────▼─────────────────────────────────────┐
│ Database-Specific Implementations │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │PostgreSQL│ │ MySQL │ │ SQLite │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘
The main entry point for database operations. Provides:
- Connection management
- Factory methods for inspectors and runners
- Database-specific configuration
Why this abstraction exists: Allows different database types to be plugged in without changing core logic.
Read-only schema introspection. Produces normalized SchemaSnapshot objects.
Why this abstraction exists:
- Enables audit mode without write permissions
- Allows dry-run mode to calculate diffs without locks
- Supports testing with mock inspectors
Executes schema changes. Supports different migration strategies.
Why this abstraction exists:
- Pluggable migration engines (SQL files, Rust code, external tools)
- Different strategies per database type
- Testing with mock runners
Creates executable migration plans from schema diffs.
Why this abstraction exists:
- Dry-run mode can show what would happen
- Validation of plans before execution
- Different planning strategies (safe ordering, dependency resolution)
Orchestrates the execution of migration plans.
Why this abstraction exists:
- Different execution strategies (transactional, non-transactional)
- Progress reporting
- Retry logic
Calculates differences between schema snapshots.
Why this abstraction exists:
- Different diff algorithms
- Three-way merge support
- Conflict detection
Stores and retrieves schema snapshots.
Why this abstraction exists:
- Multiple storage backends (filesystem, database, version control)
- Version history
- Deterministic versioning
Preview schema changes without applying them. Output formats:
- Human-readable text
- Machine-readable JSON
Implementation: Use Engine::sync_tenant() with execute=false.
Run in CI to ensure all tenants match expected schema. Exit non-zero if mismatch detected.
Implementation: Use Engine::sync_tenant() in dry-run mode for all tenants, check already_in_sync flag.
PostgreSQL (primary), MySQL, SQLite support through adapter implementations.
Implementation: Implement DatabaseAdapter, SchemaInspector, and MigrationRunner traits for each database type.
Detect drift without locks. Safe for production observability.
Implementation: Use SchemaInspector directly, no MigrationRunner needed.
Support SQL file migrations, Rust-based migrations, external tools (diesel, sqlx).
Implementation: Implement MigrationRunner trait with different strategies.
Store normalized schema snapshots. Allow diffing schema version A vs B.
Implementation: Use SnapshotStore trait implementations.
No cross-tenant leakage. Strict tenant scoping. Explicit locking strategy per tenant.
Implementation: All operations require TenantContext. MigrationRunner::acquire_lock() ensures isolation.
src/
├── lib.rs # Main library entry point
├── adapters.rs # Database adapter traits
├── diff.rs # Schema diff calculation
├── engine.rs # Main engine orchestration
├── errors.rs # Error types
├── planner.rs # Migration planning
├── executor.rs # Migration execution
├── snapshot.rs # Schema snapshots
└── cli.rs # CLI types and context
use schema_sync::prelude::*;
let adapter = PostgresAdapter::new("postgresql://...").await?;
let engine = Engine::from_adapter(/* ... */);
let tenant = TenantContext::new("tenant_123");
let result = engine.sync_tenant(&tenant, Some(&target_snapshot), true).await?;let result = engine.sync_tenant(&tenant, Some(&target_snapshot), false).await?;
if !result.diff.is_empty() {
println!("Would apply {} changes", result.diff.change_count());
}let tenants = engine.list_tenants().await?;
for tenant in tenants {
let result = engine.sync_tenant(&tenant, None, false).await?;
if !result.already_in_sync {
eprintln!("Schema mismatch for {}", tenant.id());
std::process::exit(1);
}
}- Trait-Based Extensibility: All database operations go through traits
- Separation of Concerns: Clear boundaries between inspection, planning, and execution
- Tenant Isolation: Every operation is scoped to a tenant context
- Mode-Agnostic Core: Engine doesn't know about CLI modes
- Async-First: All I/O operations are async
- Deterministic Behavior: Same inputs always produce same outputs
- Minimal Macros: Prefer traits and generics over macros
This is the foundation layer of schema-sync. It provides:
✅ Core trait definitions
✅ Data structures for schemas, diffs, and plans
✅ Architecture for extensibility
✅ Example usage patterns
🚧 Not yet implemented:
- Concrete database adapters (PostgreSQL, MySQL, SQLite)
- Concrete planner, executor, diff calculator implementations
- CLI implementation
- Snapshot store implementations
When implementing database adapters or other components:
- Implement the appropriate traits
- Keep database-specific logic in adapter implementations
- Ensure tenant isolation in all operations
- Write tests with mock implementations
- Document any database-specific behavior
MIT OR Apache-2.0