Optimize: Fix high/medium/low priority issues and apply optimizations

High Priority Fixes:
- Use configurable ClusterTimeoutMinutes for restore (was hardcoded 2 hours)
- Add comment explaining goroutine cleanup in stderr reader (cmd.Run waits)
- Add defer cancel() in cluster backup loop to prevent context leak on panic

Medium Priority Fixes:
- Standardize tick rate to 100ms for both backup and restore (consistent UX)
- Add spinnerFrame field to BackupExecutionModel for incremental updates
- Define package-level spinnerFrames constant to avoid repeated allocation

Low Priority Fixes:
- Add 30-second timeout per database in cluster cleanup loop
- Prevents indefinite hangs when dropping many databases

Optimizations:
- Pre-allocate 512 bytes in View() string builders (reduces allocations)
- Use incremental spinner frame calculation (more efficient than time-based)
- Share spinner frames array across all TUI operations

All changes are backward compatible and maintain existing behavior.
This commit is contained in:
2025-11-12 11:37:02 +00:00
parent 2ad9032b19
commit 2019591b5b
3 changed files with 25 additions and 8 deletions

View File

@@ -29,6 +29,7 @@ type BackupExecutionModel struct {
result string
startTime time.Time
details []string
spinnerFrame int
}
func NewBackupExecution(cfg *config.Config, log logger.Logger, parent tea.Model, backupType, dbName string, ratio int) BackupExecutionModel {
@@ -42,6 +43,7 @@ func NewBackupExecution(cfg *config.Config, log logger.Logger, parent tea.Model,
status: "Initializing...",
startTime: time.Now(),
details: []string{},
spinnerFrame: 0,
}
}
@@ -144,6 +146,9 @@ func (m BackupExecutionModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case backupTickMsg:
if !m.done {
// Increment spinner frame for smooth animation
m.spinnerFrame = (m.spinnerFrame + 1) % len(spinnerFrames)
// Update status based on elapsed time to show progress
elapsedSec := int(time.Since(m.startTime).Seconds())
@@ -207,6 +212,7 @@ func (m BackupExecutionModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m BackupExecutionModel) View() string {
var s strings.Builder
s.Grow(512) // Pre-allocate estimated capacity for better performance
// Clear screen with newlines and render header
s.WriteString("\n\n")
@@ -227,9 +233,7 @@ func (m BackupExecutionModel) View() string {
// Status with spinner
if !m.done {
spinner := []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
frame := int(time.Since(m.startTime).Milliseconds()/100) % len(spinner)
s.WriteString(fmt.Sprintf(" %s %s\n", spinner[frame], m.status))
s.WriteString(fmt.Sprintf(" %s %s\n", spinnerFrames[m.spinnerFrame], m.status))
} else {
s.WriteString(fmt.Sprintf(" %s\n\n", m.status))