Add reliability improvements and config persistence feature

- Implement context cleanup with sync.Once and io.Closer interface
- Add regex-based error classification for robust error handling
- Create ProcessManager with thread-safe process tracking
- Add disk space caching with 30s TTL for performance
- Implement metrics collection with structured logging
- Add config persistence (.dbbackup.conf) for directory-local settings
- Auto-save/auto-load configuration with --no-config and --no-save-config flags
- Successfully tested with 42GB d7030 database (35K large objects, 36min backup)
- All cross-platform builds working (9/10 platforms)
This commit is contained in:
2025-11-19 04:43:22 +00:00
parent ccf70db840
commit e80c16bf0e
14 changed files with 787 additions and 13 deletions

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"dbbackup/internal/backup"
"dbbackup/internal/config"
"dbbackup/internal/database"
)
@@ -43,7 +44,21 @@ func runClusterBackup(ctx context.Context) error {
engine := backup.New(cfg, log, db)
// Perform cluster backup
return engine.BackupCluster(ctx)
if err := engine.BackupCluster(ctx); err != nil {
return err
}
// Save configuration for future use (unless disabled)
if !cfg.NoSaveConfig {
localCfg := config.ConfigFromConfig(cfg)
if err := config.SaveLocalConfig(localCfg); err != nil {
log.Warn("Failed to save configuration", "error", err)
} else {
log.Info("Configuration saved to .dbbackup.conf")
}
}
return nil
}
// runSingleBackup performs a single database backup
@@ -88,7 +103,21 @@ func runSingleBackup(ctx context.Context, databaseName string) error {
engine := backup.New(cfg, log, db)
// Perform single database backup
return engine.BackupSingle(ctx, databaseName)
if err := engine.BackupSingle(ctx, databaseName); err != nil {
return err
}
// Save configuration for future use (unless disabled)
if !cfg.NoSaveConfig {
localCfg := config.ConfigFromConfig(cfg)
if err := config.SaveLocalConfig(localCfg); err != nil {
log.Warn("Failed to save configuration", "error", err)
} else {
log.Info("Configuration saved to .dbbackup.conf")
}
}
return nil
}
// runSampleBackup performs a sample database backup
@@ -154,6 +183,20 @@ func runSampleBackup(ctx context.Context, databaseName string) error {
// Create backup engine
engine := backup.New(cfg, log, db)
// Perform sample database backup
return engine.BackupSample(ctx, databaseName)
// Perform sample backup
if err := engine.BackupSample(ctx, databaseName); err != nil {
return err
}
// Save configuration for future use (unless disabled)
if !cfg.NoSaveConfig {
localCfg := config.ConfigFromConfig(cfg)
if err := config.SaveLocalConfig(localCfg); err != nil {
log.Warn("Failed to save configuration", "error", err)
} else {
log.Info("Configuration saved to .dbbackup.conf")
}
}
return nil
}

View File

@@ -38,6 +38,17 @@ For help with specific commands, use: dbbackup [command] --help`,
if cfg == nil {
return nil
}
// Load local config if not disabled
if !cfg.NoLoadConfig {
if localCfg, err := config.LoadLocalConfig(); err != nil {
log.Warn("Failed to load local config", "error", err)
} else if localCfg != nil {
config.ApplyLocalConfig(cfg, localCfg)
log.Info("Loaded configuration from .dbbackup.conf")
}
}
return cfg.SetDatabaseType(cfg.DatabaseType)
},
}
@@ -69,6 +80,8 @@ func Execute(ctx context.Context, config *config.Config, logger logger.Logger) e
rootCmd.PersistentFlags().StringVar(&cfg.SSLMode, "ssl-mode", cfg.SSLMode, "SSL mode for connections")
rootCmd.PersistentFlags().BoolVar(&cfg.Insecure, "insecure", cfg.Insecure, "Disable SSL (shortcut for --ssl-mode=disable)")
rootCmd.PersistentFlags().IntVar(&cfg.CompressionLevel, "compression", cfg.CompressionLevel, "Compression level (0-9)")
rootCmd.PersistentFlags().BoolVar(&cfg.NoSaveConfig, "no-save-config", false, "Don't save configuration after successful operations")
rootCmd.PersistentFlags().BoolVar(&cfg.NoLoadConfig, "no-config", false, "Don't load configuration from .dbbackup.conf")
return rootCmd.ExecuteContext(ctx)
}