Skip to content

Instant database snapshots for local Docker development. Like Git for your Data volumes. Supports Postgres, MySQL, & Mongo.

License

Notifications You must be signed in to change notification settings

bv-saketha-rama/db-rewind

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

28 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

db-rewind βͺ

A Lightning-Fast Time Machine for Docker Volumes

Go Version CI License Linux Only CodeRabbit Pull Request Reviews

Instantly snapshot and restore database states in <500ms


πŸ“– Overview

db-rewind is a high-performance CLI tool that acts as a "Time Machine" for local Docker volumes. It eliminates the "feedback loop latency" of re-seeding databases after destructive tests by leveraging Linux kernel syscalls for instant snapshots and atomic restores.

Why db-rewind?

  • πŸš€ Sub-second performance - Snapshot 100GB databases in <500ms
  • πŸ”’ Zero data loss - Atomic operations prevent corruption
  • πŸ’Ύ Space efficient - Copy-on-Write shares data blocks
  • 🎯 Database agnostic - Works with PostgreSQL, MySQL, MongoDB, etc.
  • πŸ›‘οΈ Safe by default - Auto-pauses containers during operations

🎯 Architecture & Data Flow

Architecture Flow

The Four Phases

db-rewind operates through four distinct, optimized phases:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  1. INSPECT     β”‚ ───> β”‚  2. FREEZE      β”‚ ───> β”‚  3. SNAPSHOT    β”‚ ───> β”‚  4. RESTORE     β”‚
β”‚  Docker Daemon  β”‚      β”‚  Pause Process  β”‚      β”‚  CoW Reflink    β”‚      β”‚  Atomic Swap    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Phase 1: Intelligence (Inspector)

  • Connects to Docker daemon via Unix socket
  • Inspects container to locate volume mount points
  • Resolves host filesystem path for operations

Phase 2: Freeze (Consistency)

  • Pauses container using Docker API
  • Flushes in-flight writes to disk
  • Prevents data corruption during operations

Phase 3: Snapshot (Reflinking)

  • Uses ioctl_ficlone syscall for Copy-on-Write
  • Creates new inodes pointing to same data blocks
  • O(1) time complexity - instant regardless of size

Phase 4: Restore (Atomic Swap)

  • Uses renameat2(RENAME_EXCHANGE) syscall
  • Atomically swaps snapshot with active volume
  • No intermediate state - zero downtime

πŸ”¬ Technical Deep Dive

Copy-on-Write (CoW) Snapshots

CoW Reflink Diagram

How it works:

  1. Original volume data sits on disk blocks
  2. Reflink creates new inode with metadata only
  3. Both inodes point to same physical data blocks
  4. Data copied only when modified (copy-on-write)

Benefits:

  • Near-instant snapshot creation
  • Minimal disk space usage
  • No performance degradation

Syscall Used:

ioctl(dest_fd, FICLONE, src_fd)

Atomic Directory Swap

Atomic Swap Diagram

How it works:

  1. Active volume and snapshot exist as separate directories
  2. renameat2() swaps both directories atomically
  3. Single syscall - no intermediate state
  4. Database never sees empty or partial state

Syscall Used:

renameat2(AT_FDCWD, path_a, AT_FDCWD, path_b, RENAME_EXCHANGE)

πŸ“‚ Project Structure

db-rewind/
β”œβ”€β”€ cmd/                        # CLI Commands
β”‚   β”œβ”€β”€ root.go                 # Base command & Docker integration
β”‚   β”œβ”€β”€ snapshot.go             # Snapshot creation logic
β”‚   └── restore.go              # Restore operation logic
β”œβ”€β”€ internal/platform/          # Platform-specific code
β”‚   β”œβ”€β”€ container/
β”‚   β”‚   └── docker.go           # Docker API wrapper
β”‚   └── fs/
β”‚       β”œβ”€β”€ cow.go              # Copy-on-Write implementation
β”‚       └── swap.go             # Atomic swap implementation
β”œβ”€β”€ docs/                       # Documentation & diagrams
β”œβ”€β”€ main.go                     # Application entry point
β”œβ”€β”€ Makefile                    # Build automation
β”œβ”€β”€ .golangci.yml              # Linter configuration
└── go.mod                      # Go module definition

πŸ“‹ Requirements

Requirement Details
OS Linux (kernel 3.15+ for renameat2)
Filesystem Btrfs, XFS (with reflink support)
Docker Installed and running
Permissions Root/sudo (for /var/lib/docker access)
Go 1.18+ (for building from source)

Filesystem Support Check

# Check if your filesystem supports reflinks
cp --reflink=always /bin/bash /tmp/test_reflink 2>&1 | grep -q "Operation not supported" && echo "❌ No reflink support" || echo "βœ… Reflinks supported"

πŸš€ Installation

Option 1: Build from Source

git clone https://github.com/fa-anony-mous/db-rewind.git
cd db-rewind
make build

Option 2: Using Make with Development Tools

# Install linting/formatting tools
make install-tools

# Format code
make fmt

# Run linters
make lint

# Build binary
make build

Option 3: Direct Go Build

go build -o db-rewind .

πŸ’» Usage

Quick Start Example

# 1. Start a database container
docker run -d --name my-postgres \
  -v pgdata:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=secret \
  postgres:15

# 2. Load your data (migrations, seeds, etc.)
psql -h localhost -U postgres -c "CREATE TABLE users (id SERIAL, name TEXT);"
psql -h localhost -U postgres -c "INSERT INTO users (name) VALUES ('Alice'), ('Bob');"

# 3. Create a snapshot
sudo ./db-rewind snapshot my-postgres clean_state
# Output: βœ“ Snapshot created in 247ms

# 4. Run destructive tests
psql -h localhost -U postgres -c "DROP TABLE users;"

# 5. Restore instantly
sudo ./db-rewind restore my-postgres clean_state
# Output: βœ“ Restore complete in 89ms

# Data is back! πŸŽ‰

Commands

Create Snapshot

sudo ./db-rewind snapshot <container_name> <snapshot_name>

Example:

sudo ./db-rewind snapshot my-postgres initial_seed

Restore Snapshot

sudo ./db-rewind restore <container_name> <snapshot_name>

Example:

sudo ./db-rewind restore my-postgres initial_seed

Inspect Container

sudo ./db-rewind <container_name>

Shows the resolved volume path.


🎯 Use Cases

1. Integration Testing

# Before test suite
sudo ./db-rewind snapshot test-db baseline

# Run tests...
npm test

# After tests
sudo ./db-rewind restore test-db baseline

2. Development Workflow

# Save clean state after migrations
sudo ./db-rewind snapshot dev-postgres migrated

# Experiment with data...
# Made a mistake? Restore instantly
sudo ./db-rewind restore dev-postgres migrated

3. A/B Testing Database States

sudo ./db-rewind snapshot my-db state_a
# Modify data...
sudo ./db-rewind snapshot my-db state_b
# Toggle between states instantly
sudo ./db-rewind restore my-db state_a

πŸ”§ Development

Code Quality

# Format code
make fmt

# Run linters
make lint

# Run tests
make test

# Full build pipeline
make all

Project Standards

  • Linting: golangci-lint with strict rules
  • Formatting: gofmt + goimports
  • Code Style: Clean, minimal comments, self-documenting
  • Error Handling: Wrapped errors with context

⚑ Performance

Operation Database Size Time Notes
Snapshot 1 GB ~200ms CoW - constant time
Snapshot 10 GB ~250ms Independent of size
Snapshot 100 GB ~300ms Metadata-only operation
Restore Any size ~100ms Single syscall

Benchmarks on Btrfs, NVMe SSD, Ryzen 5


⚠️ Important Notes

Snapshot Behavior

When you restore a snapshot, the tool performs an atomic swap:

  • The snapshot becomes the active volume
  • The dirty volume becomes the snapshot

This means snapshots are "consumed" during restore. To preserve a clean state:

# Create a backup snapshot
sudo ./db-rewind snapshot my-db clean_state
sudo ./db-rewind snapshot my-db clean_state_backup

# Restore (consumes clean_state)
sudo ./db-rewind restore my-db clean_state

# You still have clean_state_backup for future restores

Filesystem Compatibility

  • Btrfs: Full support βœ…
  • XFS: Requires kernel 4.5+ and reflink enabled βœ…
  • ext4: Not supported ❌
  • NTFS: Not supported ❌

Security

  • Requires root/sudo privileges
  • Only use in development environments
  • Not intended for production backups

🀝 Contributing

Contributions welcome! Please ensure:

  1. Code passes make lint
  2. Code is formatted with make fmt
  3. Commit messages are clear and descriptive

πŸ“„ License

MIT License - see LICENSE for details.


πŸ™ Acknowledgments

Inspired by the need for faster development feedback loops and built with:


Built with ⚑ by developers, for developers

Report Bug Β· Request Feature

About

Instant database snapshots for local Docker development. Like Git for your Data volumes. Supports Postgres, MySQL, & Mongo.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published