**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
```rust
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?;
```
### Dry-Run Mode
```rust
let result = engine.sync_tenant(&tenant, Some(&target_snapshot), false).await?;
if !result.diff.is_empty() {
println!("Would apply {} changes", result.diff.change_count());
}
```
### CI Validation
```rust
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);
}
}
```
## Design Principles
1. **Trait-Based Extensibility**: All database operations go through traits
2. **Separation of Concerns**: Clear boundaries between inspection, planning, and execution
3. **Tenant Isolation**: Every operation is scoped to a tenant context
4. **Mode-Agnostic Core**: Engine doesn't know about CLI modes
5. **Async-First**: All I/O operations are async
6. **Deterministic Behavior**: Same inputs always produce same outputs
7. **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:
1. Implement the appropriate traits
2. Keep database-specific logic in adapter implementations
3. Ensure tenant isolation in all operations
4. Write tests with mock implementations
5. Document any database-specific behavior
## License
MIT OR Apache-2.0