Files
dbbackup/internal/engine/engine.go
Alexander Renz dbb0f6f942 feat(engine): physical backup revolution - XtraBackup capabilities in pure Go
Why wrap external tools when you can BE the tool?

New physical backup engines:
• MySQL Clone Plugin - native 8.0.17+ physical backup
• Filesystem Snapshots - LVM/ZFS/Btrfs orchestration
• Binlog Streaming - continuous backup with seconds RPO
• Parallel Cloud Upload - stream directly to S3, skip local disk

Smart engine selection automatically picks the optimal strategy based on:
- MySQL version and edition
- Available filesystem features
- Database size
- Cloud connectivity

Zero external dependencies. Single binary. Enterprise capabilities.

Commercial backup vendors: we need to talk.
2025-12-13 21:21:17 +01:00

244 lines
6.8 KiB
Go

// Package engine provides backup engine abstraction for MySQL/MariaDB.
// Supports multiple backup strategies: mysqldump, clone plugin, snapshots, binlog streaming.
package engine
import (
"context"
"fmt"
"io"
"time"
)
// BackupEngine is the interface that all backup engines must implement.
// Each engine provides a different backup strategy with different tradeoffs.
type BackupEngine interface {
// Name returns the engine name (e.g., "mysqldump", "clone", "snapshot", "binlog")
Name() string
// Description returns a human-readable description
Description() string
// CheckAvailability verifies the engine can be used with the current setup
CheckAvailability(ctx context.Context) (*AvailabilityResult, error)
// Backup performs the backup operation
Backup(ctx context.Context, opts *BackupOptions) (*BackupResult, error)
// Restore restores from a backup (if supported)
Restore(ctx context.Context, opts *RestoreOptions) error
// SupportsRestore returns true if the engine supports restore operations
SupportsRestore() bool
// SupportsIncremental returns true if the engine supports incremental backups
SupportsIncremental() bool
// SupportsStreaming returns true if the engine can stream directly to cloud
SupportsStreaming() bool
}
// StreamingEngine extends BackupEngine with streaming capabilities
type StreamingEngine interface {
BackupEngine
// BackupToWriter streams the backup directly to a writer
BackupToWriter(ctx context.Context, w io.Writer, opts *BackupOptions) (*BackupResult, error)
}
// AvailabilityResult contains the result of engine availability check
type AvailabilityResult struct {
Available bool // Engine can be used
Reason string // Reason if not available
Warnings []string // Non-blocking warnings
Info map[string]string // Additional info (e.g., version, plugin status)
}
// BackupOptions contains options for backup operations
type BackupOptions struct {
// Database to backup
Database string
// Output location
OutputDir string // Local output directory
OutputFile string // Specific output file (optional, auto-generated if empty)
CloudTarget string // Cloud URI (e.g., "s3://bucket/prefix/")
StreamDirect bool // Stream directly to cloud (no local copy)
// Compression options
Compress bool
CompressFormat string // "gzip", "zstd", "lz4"
CompressLevel int // 1-9
// Performance options
Parallel int // Parallel threads/workers
// Engine-specific options
EngineOptions map[string]interface{}
// Progress reporting
ProgressFunc ProgressFunc
}
// RestoreOptions contains options for restore operations
type RestoreOptions struct {
// Source
SourcePath string // Local path
SourceCloud string // Cloud URI
// Target
TargetDir string // Target data directory
TargetHost string // Target database host
TargetPort int // Target database port
TargetUser string // Target database user
TargetPass string // Target database password
TargetDB string // Target database name
// Recovery options
RecoveryTarget *RecoveryTarget
// Engine-specific options
EngineOptions map[string]interface{}
// Progress reporting
ProgressFunc ProgressFunc
}
// RecoveryTarget specifies a point-in-time recovery target
type RecoveryTarget struct {
Type string // "time", "gtid", "position"
Time time.Time // For time-based recovery
GTID string // For GTID-based recovery
File string // For binlog position
Pos int64 // For binlog position
}
// BackupResult contains the result of a backup operation
type BackupResult struct {
// Basic info
Engine string // Engine that performed the backup
Database string // Database backed up
StartTime time.Time // When backup started
EndTime time.Time // When backup completed
Duration time.Duration
// Output files
Files []BackupFile
// Size information
TotalSize int64 // Total size of all backup files
UncompressedSize int64 // Size before compression
CompressionRatio float64
// PITR information
BinlogFile string // MySQL binlog file at backup start
BinlogPos int64 // MySQL binlog position
GTIDExecuted string // Executed GTID set
// PostgreSQL-specific (for compatibility)
WALFile string // WAL file at backup start
LSN string // Log Sequence Number
// Lock timing
LockDuration time.Duration // How long tables were locked
// Metadata
Metadata map[string]string
}
// BackupFile represents a single backup file
type BackupFile struct {
Path string // Local path or cloud key
Size int64
Checksum string // SHA-256 checksum
IsCloud bool // True if stored in cloud
}
// ProgressFunc is called to report backup progress
type ProgressFunc func(progress *Progress)
// Progress contains progress information
type Progress struct {
Stage string // Current stage (e.g., "COPYING", "COMPRESSING")
Percent float64 // Overall percentage (0-100)
BytesDone int64
BytesTotal int64
Speed float64 // Bytes per second
ETA time.Duration
Message string
}
// EngineInfo provides metadata about a registered engine
type EngineInfo struct {
Name string
Description string
Priority int // Higher = preferred when auto-selecting
Available bool // Cached availability status
}
// Registry manages available backup engines
type Registry struct {
engines map[string]BackupEngine
}
// NewRegistry creates a new engine registry
func NewRegistry() *Registry {
return &Registry{
engines: make(map[string]BackupEngine),
}
}
// Register adds an engine to the registry
func (r *Registry) Register(engine BackupEngine) {
r.engines[engine.Name()] = engine
}
// Get retrieves an engine by name
func (r *Registry) Get(name string) (BackupEngine, error) {
engine, ok := r.engines[name]
if !ok {
return nil, fmt.Errorf("engine not found: %s", name)
}
return engine, nil
}
// List returns all registered engines
func (r *Registry) List() []EngineInfo {
infos := make([]EngineInfo, 0, len(r.engines))
for name, engine := range r.engines {
infos = append(infos, EngineInfo{
Name: name,
Description: engine.Description(),
})
}
return infos
}
// GetAvailable returns engines that are currently available
func (r *Registry) GetAvailable(ctx context.Context) []EngineInfo {
var available []EngineInfo
for name, engine := range r.engines {
result, err := engine.CheckAvailability(ctx)
if err == nil && result.Available {
available = append(available, EngineInfo{
Name: name,
Description: engine.Description(),
Available: true,
})
}
}
return available
}
// DefaultRegistry is the global engine registry
var DefaultRegistry = NewRegistry()
// Register adds an engine to the default registry
func Register(engine BackupEngine) {
DefaultRegistry.Register(engine)
}
// Get retrieves an engine from the default registry
func Get(name string) (BackupEngine, error) {
return DefaultRegistry.Get(name)
}