trees: show its phases as N of M, with progress, elapsed time and ETA (closes #113)
check / check (push) Canceled after 0s

trees now runs in four phases, open, read, group and write, each under
the progress display scan uses, from the line naming the database to
the summary line. open covers a wait on a busy database. read counts
the records first, as scan's load does, then reads them against that
count; group counts the directories, and write the groups. The display
gains a unit so that each rate names what its phase counts; every scan
phase passes files and prints as before. The tests that pinned trees'
stderr now allow the phase lines between the database line and the
summary.

Judgement call: opening the database is a phase of its own, so there are four phases, not the three the issue sketches.
Judgement call: the count and the read are separate queries, as in scan's load, so during a scan the read can end slightly past or short of its total.

Model: opus-5-5
This commit is contained in:
2026-10-16 17:23:52 +00:00
parent 8dc73aa3f5
commit f838410bd0
9 changed files with 324 additions and 82 deletions
+10 -5
View File
@@ -84,6 +84,10 @@ type fileMeta struct {
hashed bool
}
// scanUnit is what every scan phase counts, as its progress display
// names it.
const scanUnit = "files"
// scanPhase names one phase of a scan with its place among all of them,
// as "[2/5] walk", for the phase's progress display. The list here is
// the one place the phases are counted.
@@ -441,7 +445,7 @@ func (s *scanState) loadIndex(ctx context.Context, roots []string) error {
// display the scan looks hung before the walk begins. Counting them
// first gives the display its total.
prog := newProgress(progressParams{
label: scanPhase("load"), total: -1,
label: scanPhase("load"), unit: scanUnit, total: -1,
waitingFor: "counting the records",
})
defer prog.finish()
@@ -492,7 +496,8 @@ func (s *scanState) walkPhase(
}
prog := newProgress(progressParams{
label: scanPhase("walk"), total: estimate, estimate: true,
label: scanPhase("walk"), unit: scanUnit, total: estimate,
estimate: true,
})
var (
@@ -683,7 +688,7 @@ func (s *scanState) readRuns(ctx context.Context, workers int,
defer pool.stop()
prog := newProgress(progressParams{
label: scanPhase(label), total: int64(len(runs)),
label: scanPhase(label), unit: scanUnit, total: int64(len(runs)),
})
defer prog.finish()
@@ -772,7 +777,7 @@ func (s *scanState) updatePhase(ctx context.Context) error {
total := len(s.batch) + len(s.sentinels) + len(deletes)
prog := newProgress(progressParams{
label: scanPhase("update"), total: int64(total),
label: scanPhase("update"), unit: scanUnit, total: int64(total),
})
defer prog.finish()
@@ -860,7 +865,7 @@ func (s *scanState) contentCandidates(
// record. The checks start there, against a count of the records
// the search returns, and so do their rate and ETA.
prog := newProgress(progressParams{
label: scanPhase("content"), total: -1,
label: scanPhase("content"), unit: scanUnit, total: -1,
waitingFor: "searching the database",
})
defer prog.finish()