Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 48f922ef6c |
@ -1,7 +1,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -12,6 +11,8 @@ import (
|
||||
"dbbackup/internal/database"
|
||||
"dbbackup/internal/engine/native"
|
||||
"dbbackup/internal/notify"
|
||||
|
||||
"github.com/klauspost/pgzip"
|
||||
)
|
||||
|
||||
// runNativeBackup executes backup using native Go engines
|
||||
@ -58,10 +59,13 @@ func runNativeBackup(ctx context.Context, db database.Database, databaseName, ba
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Wrap with compression if enabled
|
||||
// Wrap with compression if enabled (use pgzip for parallel compression)
|
||||
var writer io.Writer = file
|
||||
if cfg.CompressionLevel > 0 {
|
||||
gzWriter := gzip.NewWriter(file)
|
||||
gzWriter, err := pgzip.NewWriterLevel(file, cfg.CompressionLevel)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create gzip writer: %w", err)
|
||||
}
|
||||
defer gzWriter.Close()
|
||||
writer = gzWriter
|
||||
}
|
||||
|
||||
93
cmd/native_restore.go
Normal file
93
cmd/native_restore.go
Normal file
@ -0,0 +1,93 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"dbbackup/internal/database"
|
||||
"dbbackup/internal/engine/native"
|
||||
"dbbackup/internal/notify"
|
||||
|
||||
"github.com/klauspost/pgzip"
|
||||
)
|
||||
|
||||
// runNativeRestore executes restore using native Go engines
|
||||
func runNativeRestore(ctx context.Context, db database.Database, archivePath, targetDB string, cleanFirst, createIfMissing bool, startTime time.Time, user string) error {
|
||||
// Initialize native engine manager
|
||||
engineManager := native.NewEngineManager(cfg, log)
|
||||
|
||||
if err := engineManager.InitializeEngines(ctx); err != nil {
|
||||
return fmt.Errorf("failed to initialize native engines: %w", err)
|
||||
}
|
||||
defer engineManager.Close()
|
||||
|
||||
// Check if native engine is available for this database type
|
||||
dbType := detectDatabaseTypeFromConfig()
|
||||
if !engineManager.IsNativeEngineAvailable(dbType) {
|
||||
return fmt.Errorf("native restore engine not available for database type: %s", dbType)
|
||||
}
|
||||
|
||||
// Open archive file
|
||||
file, err := os.Open(archivePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open archive: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Detect if file is gzip compressed
|
||||
var reader io.Reader = file
|
||||
if isGzipFile(archivePath) {
|
||||
gzReader, err := pgzip.NewReader(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create gzip reader: %w", err)
|
||||
}
|
||||
defer gzReader.Close()
|
||||
reader = gzReader
|
||||
}
|
||||
|
||||
log.Info("Starting native restore",
|
||||
"archive", archivePath,
|
||||
"database", targetDB,
|
||||
"engine", dbType,
|
||||
"clean_first", cleanFirst,
|
||||
"create_if_missing", createIfMissing)
|
||||
|
||||
// Perform restore using native engine
|
||||
if err := engineManager.RestoreWithNativeEngine(ctx, reader, targetDB); err != nil {
|
||||
auditLogger.LogRestoreFailed(user, targetDB, err)
|
||||
if notifyManager != nil {
|
||||
notifyManager.Notify(notify.NewEvent(notify.EventRestoreFailed, notify.SeverityError, "Native restore failed").
|
||||
WithDatabase(targetDB).
|
||||
WithError(err))
|
||||
}
|
||||
return fmt.Errorf("native restore failed: %w", err)
|
||||
}
|
||||
|
||||
restoreDuration := time.Since(startTime)
|
||||
|
||||
log.Info("Native restore completed successfully",
|
||||
"database", targetDB,
|
||||
"duration", restoreDuration,
|
||||
"engine", dbType)
|
||||
|
||||
// Audit log: restore completed
|
||||
auditLogger.LogRestoreComplete(user, targetDB, restoreDuration)
|
||||
|
||||
// Notify: restore completed
|
||||
if notifyManager != nil {
|
||||
notifyManager.Notify(notify.NewEvent(notify.EventRestoreCompleted, notify.SeverityInfo, "Native restore completed").
|
||||
WithDatabase(targetDB).
|
||||
WithDuration(restoreDuration).
|
||||
WithDetail("engine", dbType))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// isGzipFile checks if file has gzip extension
|
||||
func isGzipFile(path string) bool {
|
||||
return len(path) > 3 && path[len(path)-3:] == ".gz"
|
||||
}
|
||||
@ -720,6 +720,23 @@ func runRestoreSingle(cmd *cobra.Command, args []string) error {
|
||||
WithDetail("archive", filepath.Base(archivePath)))
|
||||
}
|
||||
|
||||
// Check if native engine should be used for restore
|
||||
if cfg.UseNativeEngine {
|
||||
log.Info("Using native engine for restore", "database", targetDB)
|
||||
err = runNativeRestore(ctx, db, archivePath, targetDB, restoreClean, restoreCreate, startTime, user)
|
||||
|
||||
if err != nil && cfg.FallbackToTools {
|
||||
log.Warn("Native engine restore failed, falling back to external tools", "error", err)
|
||||
// Continue with tool-based restore below
|
||||
} else {
|
||||
// Native engine succeeded or no fallback configured
|
||||
if err == nil {
|
||||
log.Info("[OK] Restore completed successfully (native engine)", "database", targetDB)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := engine.RestoreSingle(ctx, archivePath, targetDB, restoreClean, restoreCreate); err != nil {
|
||||
auditLogger.LogRestoreFailed(user, targetDB, err)
|
||||
// Notify: restore failed
|
||||
|
||||
@ -94,6 +94,11 @@ func NewSettingsModel(cfg *config.Config, log logger.Logger, parent tea.Model) S
|
||||
c.CPUWorkloadType = workloads[nextIdx]
|
||||
|
||||
// Recalculate Jobs and DumpJobs based on workload type
|
||||
// If CPUInfo is nil, try to detect it first
|
||||
if c.CPUInfo == nil && c.AutoDetectCores {
|
||||
_ = c.OptimizeForCPU() // This will detect CPU and set CPUInfo
|
||||
}
|
||||
|
||||
if c.CPUInfo != nil && c.AutoDetectCores {
|
||||
switch c.CPUWorkloadType {
|
||||
case "cpu-intensive":
|
||||
|
||||
Reference in New Issue
Block a user