Fix: Interactive restore now shows dynamic status updates during operation
Issue: Interactive cluster restore showed 'Status: Initializing...' throughout the entire restore process, making it appear stuck even though restore was working. Root cause: - Status and phase were set once in NewRestoreExecution() - Never updated during the restore process - Only changed to 'Completed' or 'Failed' at the end - No visual feedback about what stage of restore was running Solution: Time-based status progression Added logic in Update() tick handler to change status based on elapsed time: - 0-2 sec: 'Initializing restore...' / Phase: Starting - 2-5 sec: Context-aware status: - If cleanup: 'Cleaning N existing database(s)...' / Phase: Cleanup - If cluster: 'Extracting cluster archive...' / Phase: Extraction - If single: 'Preparing restore...' / Phase: Preparation - 5-10 sec: - If cluster: 'Restoring global objects...' / Phase: Globals - If single: 'Restoring database...' / Phase: Restore - 10+ sec: 'Restoring [cluster] databases...' / Phase: Restore Benefits: - User sees the restore is progressing through stages - Different status messages for cluster vs single database restore - Shows cleanup phase when enabled - Spinner + changing status = clear visual feedback - Better user experience during long-running restores Note: These are estimated phases since the restore engine runs in silent mode (no stdout interference with TUI). Actual operation may be faster or slower than time estimates, but provides much better UX than static 'Initializing'.
This commit is contained in:
@@ -175,6 +175,43 @@ func (m RestoreExecutionModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
if !m.done {
|
if !m.done {
|
||||||
m.spinnerFrame = (m.spinnerFrame + 1) % len(m.spinnerFrames)
|
m.spinnerFrame = (m.spinnerFrame + 1) % len(m.spinnerFrames)
|
||||||
m.elapsed = time.Since(m.startTime)
|
m.elapsed = time.Since(m.startTime)
|
||||||
|
|
||||||
|
// Update status based on elapsed time to show progress
|
||||||
|
// This provides visual feedback even though we don't have real-time progress
|
||||||
|
elapsedSec := int(m.elapsed.Seconds())
|
||||||
|
|
||||||
|
if elapsedSec < 2 {
|
||||||
|
m.status = "Initializing restore..."
|
||||||
|
m.phase = "Starting"
|
||||||
|
} else if elapsedSec < 5 {
|
||||||
|
if m.cleanClusterFirst && len(m.existingDBs) > 0 {
|
||||||
|
m.status = fmt.Sprintf("Cleaning %d existing database(s)...", len(m.existingDBs))
|
||||||
|
m.phase = "Cleanup"
|
||||||
|
} else if m.restoreType == "restore-cluster" {
|
||||||
|
m.status = "Extracting cluster archive..."
|
||||||
|
m.phase = "Extraction"
|
||||||
|
} else {
|
||||||
|
m.status = "Preparing restore..."
|
||||||
|
m.phase = "Preparation"
|
||||||
|
}
|
||||||
|
} else if elapsedSec < 10 {
|
||||||
|
if m.restoreType == "restore-cluster" {
|
||||||
|
m.status = "Restoring global objects..."
|
||||||
|
m.phase = "Globals"
|
||||||
|
} else {
|
||||||
|
m.status = fmt.Sprintf("Restoring database '%s'...", m.targetDB)
|
||||||
|
m.phase = "Restore"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if m.restoreType == "restore-cluster" {
|
||||||
|
m.status = "Restoring cluster databases..."
|
||||||
|
m.phase = "Restore"
|
||||||
|
} else {
|
||||||
|
m.status = fmt.Sprintf("Restoring database '%s'...", m.targetDB)
|
||||||
|
m.phase = "Restore"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return m, restoreTickCmd()
|
return m, restoreTickCmd()
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
@@ -199,7 +236,7 @@ func (m RestoreExecutionModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.elapsed = msg.elapsed
|
m.elapsed = msg.elapsed
|
||||||
|
|
||||||
if m.err == nil {
|
if m.err == nil {
|
||||||
m.status = "Completed"
|
m.status = "Restore completed successfully"
|
||||||
m.phase = "Done"
|
m.phase = "Done"
|
||||||
m.progress = 100
|
m.progress = 100
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user