Skip to content

Production-grade schema synchronization for multi-tenant databases in Rust.

License

Notifications You must be signed in to change notification settings

exeebit/schema-sync

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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 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

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

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

About

Production-grade schema synchronization for multi-tenant databases in Rust.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages