Feature: Interactive CPU workload selection in TUI menu
Added interactive workload type selector similar to database type selector: - Three workload options: Balanced | CPU-Intensive | I/O-Intensive - Switch with Shift+←/→ arrows or 'w' key - Automatically adjusts Jobs and DumpJobs based on selection: * CPU-Intensive: More parallelism (2x physical cores) * I/O-Intensive: Less parallelism (0.5x physical cores) * Balanced: Standard parallelism (1x physical cores) UI shows current selection with description: - Balanced (General purpose) - CPU-Intensive (More parallelism) - I/O-Intensive (Less parallelism) Real-time feedback shows adjusted Jobs/DumpJobs values. Complements existing --cpu-workload CLI flag with interactive UX.
This commit is contained in:
@@ -48,16 +48,24 @@ type dbTypeOption struct {
|
||||
value string
|
||||
}
|
||||
|
||||
type workloadOption struct {
|
||||
label string
|
||||
value string
|
||||
desc string
|
||||
}
|
||||
|
||||
// MenuModel represents the simple menu state
|
||||
type MenuModel struct {
|
||||
choices []string
|
||||
cursor int
|
||||
config *config.Config
|
||||
logger logger.Logger
|
||||
quitting bool
|
||||
message string
|
||||
dbTypes []dbTypeOption
|
||||
dbTypeCursor int
|
||||
choices []string
|
||||
cursor int
|
||||
config *config.Config
|
||||
logger logger.Logger
|
||||
quitting bool
|
||||
message string
|
||||
dbTypes []dbTypeOption
|
||||
dbTypeCursor int
|
||||
workloads []workloadOption
|
||||
workloadCursor int
|
||||
|
||||
// Background operations
|
||||
ctx context.Context
|
||||
@@ -73,6 +81,12 @@ func NewMenuModel(cfg *config.Config, log logger.Logger) MenuModel {
|
||||
{label: "MariaDB", value: "mariadb"},
|
||||
}
|
||||
|
||||
workloads := []workloadOption{
|
||||
{label: "Balanced", value: "balanced", desc: "General purpose"},
|
||||
{label: "CPU-Intensive", value: "cpu-intensive", desc: "More parallelism"},
|
||||
{label: "I/O-Intensive", value: "io-intensive", desc: "Less parallelism"},
|
||||
}
|
||||
|
||||
dbCursor := 0
|
||||
if cfg.DatabaseType == "mysql" {
|
||||
dbCursor = 1
|
||||
@@ -80,6 +94,13 @@ func NewMenuModel(cfg *config.Config, log logger.Logger) MenuModel {
|
||||
dbCursor = 2
|
||||
}
|
||||
|
||||
workloadCursor := 0
|
||||
if cfg.CPUWorkloadType == "cpu-intensive" {
|
||||
workloadCursor = 1
|
||||
} else if cfg.CPUWorkloadType == "io-intensive" {
|
||||
workloadCursor = 2
|
||||
}
|
||||
|
||||
model := MenuModel{
|
||||
choices: []string{
|
||||
"Single Database Backup",
|
||||
@@ -97,12 +118,14 @@ func NewMenuModel(cfg *config.Config, log logger.Logger) MenuModel {
|
||||
"Clear Operation History",
|
||||
"Quit",
|
||||
},
|
||||
config: cfg,
|
||||
logger: log,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
dbTypes: dbTypes,
|
||||
dbTypeCursor: dbCursor,
|
||||
config: cfg,
|
||||
logger: log,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
dbTypes: dbTypes,
|
||||
dbTypeCursor: dbCursor,
|
||||
workloads: workloads,
|
||||
workloadCursor: workloadCursor,
|
||||
}
|
||||
|
||||
return model
|
||||
@@ -143,6 +166,24 @@ func (m MenuModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.applyDatabaseSelection()
|
||||
}
|
||||
|
||||
case "shift+left", "H":
|
||||
if m.workloadCursor > 0 {
|
||||
m.workloadCursor--
|
||||
m.applyWorkloadSelection()
|
||||
}
|
||||
|
||||
case "shift+right", "L":
|
||||
if m.workloadCursor < len(m.workloads)-1 {
|
||||
m.workloadCursor++
|
||||
m.applyWorkloadSelection()
|
||||
}
|
||||
|
||||
case "w":
|
||||
if len(m.workloads) > 0 {
|
||||
m.workloadCursor = (m.workloadCursor + 1) % len(m.workloads)
|
||||
m.applyWorkloadSelection()
|
||||
}
|
||||
|
||||
case "up", "k":
|
||||
if m.cursor > 0 {
|
||||
m.cursor--
|
||||
@@ -218,6 +259,21 @@ func (m MenuModel) View() string {
|
||||
selector := fmt.Sprintf("Target Engine: %s", strings.Join(options, menuStyle.Render(" | ")))
|
||||
s += dbSelectorLabelStyle.Render(selector) + "\n"
|
||||
hint := infoStyle.Render("Switch with ←/→ or t • Cluster backup requires PostgreSQL")
|
||||
s += hint + "\n"
|
||||
}
|
||||
|
||||
if len(m.workloads) > 0 {
|
||||
options := make([]string, len(m.workloads))
|
||||
for i, opt := range m.workloads {
|
||||
if m.workloadCursor == i {
|
||||
options[i] = menuSelectedStyle.Render(fmt.Sprintf("%s (%s)", opt.label, opt.desc))
|
||||
} else {
|
||||
options[i] = menuStyle.Render(opt.label)
|
||||
}
|
||||
}
|
||||
selector := fmt.Sprintf("CPU Workload: %s", strings.Join(options, menuStyle.Render(" | ")))
|
||||
s += dbSelectorLabelStyle.Render(selector) + "\n"
|
||||
hint := infoStyle.Render("Switch with Shift+←/→ or w • Affects parallelism and compression")
|
||||
s += hint + "\n\n"
|
||||
}
|
||||
|
||||
@@ -346,6 +402,52 @@ func (m *MenuModel) applyDatabaseSelection() {
|
||||
}
|
||||
}
|
||||
|
||||
// applyWorkloadSelection updates the config with the selected workload type
|
||||
func (m *MenuModel) applyWorkloadSelection() {
|
||||
if m.config == nil {
|
||||
return
|
||||
}
|
||||
if m.workloadCursor < 0 || m.workloadCursor >= len(m.workloads) {
|
||||
return
|
||||
}
|
||||
|
||||
selection := m.workloads[m.workloadCursor]
|
||||
m.config.CPUWorkloadType = selection.value
|
||||
|
||||
// Recalculate optimal settings based on workload type
|
||||
if m.config.CPUInfo != nil && m.config.AutoDetectCores {
|
||||
// Recalculate Jobs and DumpJobs based on new workload type
|
||||
switch selection.value {
|
||||
case "cpu-intensive":
|
||||
// More parallelism for CPU-bound tasks
|
||||
m.config.Jobs = m.config.CPUInfo.PhysicalCores * 2
|
||||
m.config.DumpJobs = m.config.CPUInfo.PhysicalCores
|
||||
case "io-intensive":
|
||||
// Less parallelism to avoid I/O contention
|
||||
m.config.Jobs = m.config.CPUInfo.PhysicalCores / 2
|
||||
if m.config.Jobs < 1 {
|
||||
m.config.Jobs = 1
|
||||
}
|
||||
m.config.DumpJobs = 2
|
||||
default: // balanced
|
||||
m.config.Jobs = m.config.CPUInfo.PhysicalCores
|
||||
m.config.DumpJobs = m.config.CPUInfo.PhysicalCores / 2
|
||||
if m.config.DumpJobs < 2 {
|
||||
m.config.DumpJobs = 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m.message = successStyle.Render(fmt.Sprintf("⚡ CPU workload set to %s (Jobs: %d, DumpJobs: %d)",
|
||||
selection.label, m.config.Jobs, m.config.DumpJobs))
|
||||
if m.logger != nil {
|
||||
m.logger.Info("updated CPU workload type",
|
||||
"type", m.config.CPUWorkloadType,
|
||||
"jobs", m.config.Jobs,
|
||||
"dump_jobs", m.config.DumpJobs)
|
||||
}
|
||||
}
|
||||
|
||||
// RunInteractiveMenu starts the simple TUI
|
||||
func RunInteractiveMenu(cfg *config.Config, log logger.Logger) error {
|
||||
m := NewMenuModel(cfg, log)
|
||||
|
||||
Reference in New Issue
Block a user