From 57efa64f18deea5f0629297dfa2a22a64985e60f Mon Sep 17 00:00:00 2001 From: sneak Date: Sat, 25 Jul 2026 14:28:11 +0700 Subject: [PATCH] Show progress while loading the record index On a database with tens of millions of records, indexing the existing rows before the walk takes real single-core time with no output, which is indistinguishable from a hang. Give the load its own spinner, and render every phase display the moment the phase starts instead of waiting for its first completed item. --- README.md | 8 +++++--- progress.go | 3 +++ scan.go | 7 +++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1ac1476..d7bd657 100644 --- a/README.md +++ b/README.md @@ -395,9 +395,11 @@ all dupe rows) in human units. Use the progress-bar library for all scan progress; rendering in the style of `pv` is the model. All progress goes to stderr. -Each phase gets its own display. The walk has no known total while -running: show a live file count, rate, and elapsed time -(spinner-style, no percentage or ETA). The hash and update phases +Each phase gets its own display, rendered the moment the phase +starts — a scan must never look hung. Loading the existing-record +index (`load`) and the walk have no known totals while running: show +a live count, rate, and elapsed time (spinner-style, no percentage or +ETA). The hash and update phases have exact totals — only files that actually need hashing appear in the hash total, so its ETA is meaningful. Required elements for the bars with known totals: diff --git a/progress.go b/progress.go index a168bf5..ecb0585 100644 --- a/progress.go +++ b/progress.go @@ -65,6 +65,9 @@ func newProgress(label string, total int64) *progress { progressbar.OptionSetItsString("files"), progressbar.OptionSetElapsedTime(true), progressbar.OptionThrottle(barThrottle), + // Render at zero immediately: a phase must be visible the + // moment it starts, even before its first item completes. + progressbar.OptionSetRenderBlankState(true), } if total >= 0 { opts = append(opts, diff --git a/scan.go b/scan.go index e618351..4e74278 100644 --- a/scan.go +++ b/scan.go @@ -186,10 +186,17 @@ func syncScan(db *sql.DB, roots []string, workers int, // can be recognized as a possible duplicate of a tree scanned // separately into the same database. func (s *scanState) loadIndex(roots []string) error { + // Indexing tens of millions of records takes real time; without a + // display the scan looks hung before the walk begins. + prog := newProgress("load", -1) + defer prog.finish() + s.existing = make(map[string]fileMeta) return loadFileMeta(s.db, func(path string, size, mtime int64, hashed bool) { + prog.increment() + if underAnyRoot(path, roots) { s.existing[path] = fileMeta{ size: size, mtime: mtime, hashed: hashed,