Files
dbbackup/internal/config/profile.go
Alexander Renz 343a8b782d
All checks were successful
CI/CD / Test (push) Successful in 1m17s
CI/CD / Lint (push) Successful in 1m26s
CI/CD / Build & Release (push) Successful in 4m1s
Fix Ctrl+C not working in TUI backup/restore operations
Critical Bug Fix: Context cancellation was broken in TUI mode

Issue:
- executeBackupWithTUIProgress() and executeRestoreWithTUIProgress()
  created new contexts with WithCancel(parentCtx)
- When user pressed Ctrl+C, model.cancel() was called on parent context
- But the execution functions had their own separate context that didn't get cancelled
- Result: Ctrl+C had no effect, operations couldn't be interrupted

Fix:
- Use parent context directly instead of creating new one
- Parent context is already cancellable from the model
- Now Ctrl+C properly propagates to running backup/restore operations

Affected:
- TUI cluster backup (internal/tui/backup_exec.go)
- TUI cluster restore (internal/tui/restore_exec.go)

This restores critical user control over long-running operations.
2026-01-20 12:05:23 +01:00

129 lines
3.9 KiB
Go

package config
import (
"fmt"
"strings"
)
// RestoreProfile defines resource settings for restore operations
type RestoreProfile struct {
Name string
ParallelDBs int // Number of databases to restore in parallel
Jobs int // Parallel decompression jobs
DisableProgress bool // Disable progress indicators to reduce overhead
MemoryConservative bool // Use memory-conservative settings
}
// GetRestoreProfile returns the profile settings for a given profile name
func GetRestoreProfile(profileName string) (*RestoreProfile, error) {
profileName = strings.ToLower(strings.TrimSpace(profileName))
switch profileName {
case "conservative":
return &RestoreProfile{
Name: "conservative",
ParallelDBs: 1, // Single-threaded restore
Jobs: 1, // Single-threaded decompression
DisableProgress: false,
MemoryConservative: true,
}, nil
case "balanced", "":
return &RestoreProfile{
Name: "balanced",
ParallelDBs: 0, // Use config default or auto-detect
Jobs: 0, // Use config default or auto-detect
DisableProgress: false,
MemoryConservative: false,
}, nil
case "aggressive", "performance", "max":
return &RestoreProfile{
Name: "aggressive",
ParallelDBs: -1, // Auto-detect based on resources
Jobs: -1, // Auto-detect based on CPU
DisableProgress: false,
MemoryConservative: false,
}, nil
case "potato":
// Easter egg: same as conservative but with a fun name
return &RestoreProfile{
Name: "potato",
ParallelDBs: 1,
Jobs: 1,
DisableProgress: false,
MemoryConservative: true,
}, nil
default:
return nil, fmt.Errorf("unknown profile: %s (valid: conservative, balanced, aggressive)", profileName)
}
}
// ApplyProfile applies profile settings to config, respecting explicit user overrides
func ApplyProfile(cfg *Config, profileName string, explicitJobs, explicitParallelDBs int) error {
profile, err := GetRestoreProfile(profileName)
if err != nil {
return err
}
// Show profile being used
if cfg.Debug {
fmt.Printf("Using restore profile: %s\n", profile.Name)
if profile.MemoryConservative {
fmt.Println("Memory-conservative mode enabled")
}
}
// Apply profile settings only if not explicitly overridden
if explicitJobs == 0 && profile.Jobs > 0 {
cfg.Jobs = profile.Jobs
}
if explicitParallelDBs == 0 && profile.ParallelDBs != 0 {
cfg.ClusterParallelism = profile.ParallelDBs
}
// Store profile name
cfg.ResourceProfile = profile.Name
// Conservative profile implies large DB mode settings
if profile.MemoryConservative {
cfg.LargeDBMode = true
}
return nil
}
// GetProfileDescription returns a human-readable description of the profile
func GetProfileDescription(profileName string) string {
profile, err := GetRestoreProfile(profileName)
if err != nil {
return "Unknown profile"
}
switch profile.Name {
case "conservative":
return "Conservative: --parallel=1, single-threaded, minimal memory usage. Best for resource-constrained servers or when other services are running."
case "potato":
return "Potato Mode: Same as conservative, for servers running on a potato 🥔"
case "balanced":
return "Balanced: Auto-detect resources, moderate parallelism. Good default for most scenarios."
case "aggressive":
return "Aggressive: Maximum parallelism, all available resources. Best for dedicated database servers with ample resources."
default:
return profile.Name
}
}
// ListProfiles returns a list of all available profiles with descriptions
func ListProfiles() map[string]string {
return map[string]string{
"conservative": GetProfileDescription("conservative"),
"balanced": GetProfileDescription("balanced"),
"aggressive": GetProfileDescription("aggressive"),
"potato": GetProfileDescription("potato"),
}
}