Developer: s4gor
Github: https://github.com/s4gor
Schema Sync
Production-grade schema synchronization for multi-tenant databases.
Overview
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.
Architecture
Design Philosophy
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
Core Components
┌─────────────────────────────────────────────────────────────┐
│ 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 │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘
Key Abstractions
1. DatabaseAdapter
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.
2. SchemaInspector
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
3. MigrationRunner
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
4. Planner
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)
5. Executor
Orchestrates the execution of migration plans.
Why this abstraction exists:
- Different execution strategies (transactional, non-transactional)
- Progress reporting
- Retry logic
6. DiffCalculator
Calculates differences between schema snapshots.
Why this abstraction exists:
- Different diff algorithms
- Three-way merge support
- Conflict detection
7. SnapshotStore
Stores and retrieves schema snapshots.
Why this abstraction exists:
- Multiple storage backends (filesystem, database, version control)
- Version history
- Deterministic versioning
Future Extensions
1. Dry-Run / Diff Mode
Preview schema changes without applying them. Output formats:
- Human-readable text
- Machine-readable JSON
Implementation: Use Engine::sync_tenant() with execute=false.
2. CI / Validation Mode
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.
3. Multi-Database Support
PostgreSQL (primary), MySQL, SQLite support through adapter implementations.
Implementation: Implement DatabaseAdapter, SchemaInspector, and MigrationRunner traits for each database type.
4. Read-Only Audit Mode
Detect drift without locks. Safe for production observability.
Implementation: Use SchemaInspector directly, no MigrationRunner needed.
5. Pluggable Migration Engine
Support SQL file migrations, Rust-based migrations, external tools (diesel, sqlx).
Implementation: Implement MigrationRunner trait with different strategies.
6. Schema Snapshot System
Store normalized schema snapshots. Allow diffing schema version A vs B.
Implementation: Use SnapshotStore trait implementations.
7. Tenant Isolation Guarantees
No cross-tenant leakage. Strict tenant scoping. Explicit locking strategy per tenant.
Implementation: All operations require TenantContext. MigrationRunner::acquire_lock() ensures isolation.
Module Structure
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
Usage
Basic Sync
use *;
let adapter = new.await?;
let engine = from_adapter;
let tenant = new;
let result = engine.sync_tenant.await?;
Dry-Run Mode
let result = engine.sync_tenant.await?;
if !result.diff.is_empty
CI Validation
let tenants = engine.list_tenants.await?;
for tenant in tenants
Design Principles
- 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
Status
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
Contributing
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
License
MIT OR Apache-2.0