v0.147.2 — 4c follow-up 2: when NO counter can move, say what is being worked on
The v0.147.1 file-count fallback fixed the incremental case but not the one the demo box actually hits. Watching a second real run: bookstack reported clean byte progress (100%, 154.0 MB, 7/7 files — the byte path works), while immich sat at files_done 1 of 46, bytes_done 0, for 42 seconds. restic 0.14 only counts a file into bytes_done/files_done when it COMPLETES, so an app dominated by a single large archive (immich's ~430MB volume tar) freezes both counters. No percentage can move in that window, so stop trying to fake one. restic keeps reporting current_files and seconds_elapsed throughout. The card now names the file being processed and the elapsed time: "1 / 46 fájl (430.2 MB) · feldolgozás alatt: immich_upload.tar · 42 mp". "Working on this file for 42 seconds" is a completely different message from "0%", and it is the honest one. The last known current_files value persists across ticks that omit it (restic does not send it every tick, and blanking the label every other second is its own flicker); switching app clears it so one app's file is never shown against another. Both pinned by tests, with the real 42-second status line shape. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nn3VgQk9iwEGgyx6QJ2NvE
This commit is contained in:
@@ -44,6 +44,14 @@ type OffboxProgress struct {
|
||||
// in that case, so the page falls back to them.
|
||||
FilesDone int64 `json:"files_done"`
|
||||
TotalFiles int64 `json:"total_files"`
|
||||
// CurrentFile/ElapsedSec are the last resort, and on real data the most important fields here.
|
||||
// restic 0.14 only counts a file into files_done/bytes_done when it COMPLETES, so a single
|
||||
// dominant file freezes both counters: measured on the demo box, immich sat at files_done 1 of 46
|
||||
// and bytes_done 0 for 42 seconds while restic worked on one ~430MB volume tar. No percentage can
|
||||
// move during that window. What CAN be shown truthfully is which file is being processed and how
|
||||
// long it has been going — "working on X, 42s" is a completely different message from "0%".
|
||||
CurrentFile string `json:"current_file"`
|
||||
ElapsedSec int64 `json:"elapsed_sec"`
|
||||
}
|
||||
|
||||
// resticStatusLine is the subset of restic's `--json` status object we consume. restic emits several
|
||||
@@ -59,21 +67,25 @@ type OffboxProgress struct {
|
||||
// Note every numeric field except percent_done is `omitempty` on restic's side: a zero simply is not
|
||||
// in the JSON. That is why an incremental run reports no bytes_done at all rather than an explicit 0.
|
||||
type resticStatusLine struct {
|
||||
MessageType string `json:"message_type"`
|
||||
PercentDone float64 `json:"percent_done"` // 0..1
|
||||
TotalBytes int64 `json:"total_bytes"`
|
||||
BytesDone int64 `json:"bytes_done"`
|
||||
TotalFiles int64 `json:"total_files"`
|
||||
FilesDone int64 `json:"files_done"`
|
||||
MessageType string `json:"message_type"`
|
||||
PercentDone float64 `json:"percent_done"` // 0..1
|
||||
TotalBytes int64 `json:"total_bytes"`
|
||||
BytesDone int64 `json:"bytes_done"`
|
||||
TotalFiles int64 `json:"total_files"`
|
||||
FilesDone int64 `json:"files_done"`
|
||||
CurrentFiles []string `json:"current_files"`
|
||||
SecondsElapsed int64 `json:"seconds_elapsed"`
|
||||
}
|
||||
|
||||
// resticProgress is one parsed status line.
|
||||
type resticProgress struct {
|
||||
Percent float64 // 0..100
|
||||
BytesDone int64
|
||||
TotalBytes int64
|
||||
FilesDone int64
|
||||
TotalFiles int64
|
||||
Percent float64 // 0..100
|
||||
BytesDone int64
|
||||
TotalBytes int64
|
||||
FilesDone int64
|
||||
TotalFiles int64
|
||||
CurrentFile string
|
||||
ElapsedSec int64
|
||||
}
|
||||
|
||||
// parseResticStatus parses ONE line of restic --json output.
|
||||
@@ -99,9 +111,14 @@ func parseResticStatus(line string) (resticProgress, bool) {
|
||||
if pct > 100 {
|
||||
pct = 100
|
||||
}
|
||||
cur := ""
|
||||
if len(s.CurrentFiles) > 0 {
|
||||
cur = s.CurrentFiles[0]
|
||||
}
|
||||
return resticProgress{
|
||||
Percent: pct, BytesDone: s.BytesDone, TotalBytes: s.TotalBytes,
|
||||
FilesDone: s.FilesDone, TotalFiles: s.TotalFiles,
|
||||
CurrentFile: cur, ElapsedSec: s.SecondsElapsed,
|
||||
}, true
|
||||
}
|
||||
|
||||
@@ -135,6 +152,7 @@ func (p *offboxProgressState) setApp(app string) {
|
||||
p.cur.CurrentApp = app
|
||||
p.cur.Percent, p.cur.BytesDone, p.cur.TotalBytes = 0, 0, 0
|
||||
p.cur.FilesDone, p.cur.TotalFiles = 0, 0
|
||||
p.cur.CurrentFile, p.cur.ElapsedSec = "", 0
|
||||
p.cur.DoneHuman, p.cur.TotalHuman = "", ""
|
||||
}
|
||||
p.mu.Unlock()
|
||||
@@ -145,6 +163,12 @@ func (p *offboxProgressState) update(r resticProgress) {
|
||||
if p.live {
|
||||
p.cur.Percent, p.cur.BytesDone, p.cur.TotalBytes = r.Percent, r.BytesDone, r.TotalBytes
|
||||
p.cur.FilesDone, p.cur.TotalFiles = r.FilesDone, r.TotalFiles
|
||||
p.cur.ElapsedSec = r.ElapsedSec
|
||||
// Keep the last KNOWN current file: restic omits current_files on some status ticks, and
|
||||
// blanking the label every other second is its own kind of flicker.
|
||||
if r.CurrentFile != "" {
|
||||
p.cur.CurrentFile = r.CurrentFile
|
||||
}
|
||||
p.cur.DoneHuman, p.cur.TotalHuman = humanizeBytes(r.BytesDone), humanizeBytes(r.TotalBytes)
|
||||
}
|
||||
p.mu.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user