Skip to content

Commit c4de165

Browse files
restore /NNN, pct, and remain — removing byte extrapolation. Updating backup and restore stats so stats_line_elapsed_seg_w always uses max(..., len(elapsed_seg)).
1 parent eec22f7 commit c4de165

4 files changed

Lines changed: 16 additions & 42 deletions

File tree

src/bacchus/persistence.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class RuntimeState:
3535
stats_line_last_seg_w: int = 0
3636
stats_line_avg_seg_w: int = 0
3737
stats_line_compr_seg_w: int = 0
38-
# First full incremental stats line locks ``elapsed..`` column width from that line's remain.
38+
# Legacy: elapsed column width no longer seeded from remain (unused).
3939
stats_line_elapsed_seeded: bool = False
4040
chunk_index: int = 0
4141
mv_group_open: bool = False

src/bacchus/restore_chunked.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ def record_prompt_idle(secs: int) -> None:
308308

309309
mx = max(mx, volume_supply.max_chunk_seq_across_roots(search_roots, cfg.basename))
310310
st_pre = persistence.load(tmp_runtime)
311+
# Outer chunk total: max index seen across all search roots (grows when new dirs are added).
311312
st_pre.archive_volumes = max(st_pre.archive_volumes, mx, processed_chunks + 1)
312313
st_pre.source_size_total = volume_supply.total_archive_kb_on_roots(search_roots)
313314
persistence.save(tmp_runtime, st_pre)

src/bacchus/stats.py

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ def _emit_incremental_stats_full_line(
174174
Single layout path for backup and restore full incremental rows.
175175
176176
Requires :attr:`~bacchus.persistence.RuntimeState.stats_line_*` widths to already
177-
reflect this row's segments (running maxima updated before calling).
177+
reflect this row's segments (monotonic running maxima updated before calling).
178178
"""
179179
tail = STATS_INCREMENTAL_COL_GAP.join(
180180
[
@@ -252,14 +252,7 @@ def incremental_stats_backup(
252252

253253
el_txt = duration_readable(elapsed_time)
254254
elapsed_seg = "elapsed.." + el_txt
255-
if not state.stats_line_elapsed_seeded:
256-
seed_elapsed = "elapsed.." + duration_readable(remain_time)
257-
state.stats_line_elapsed_seg_w = max(
258-
state.stats_line_elapsed_seg_w,
259-
len(seed_elapsed),
260-
len(elapsed_seg),
261-
)
262-
state.stats_line_elapsed_seeded = True
255+
state.stats_line_elapsed_seg_w = max(state.stats_line_elapsed_seg_w, len(elapsed_seg))
263256

264257
inc_txt = duration_readable(incremental_time)
265258
state.incremental_text_size_running = max(state.incremental_text_size_running, len(inc_txt))
@@ -318,8 +311,9 @@ def incremental_stats_restore(
318311
tier3_inner_mv_vol: int | None = None,
319312
) -> None:
320313
"""Restore incremental row; full lines use :func:`_emit_incremental_stats_full_line` like backup."""
321-
volume_cap = max(_chunked_volume_total_display(state, tar_volume), state.archive_volumes)
322-
state.stats_volume_cap_chars_max = max(state.stats_volume_cap_chars_max, len(str(volume_cap)))
314+
# Chunk total comes only from scanned archive dirs (updated when roots change), not byte estimates.
315+
vol_total = max(state.archive_volumes, tar_volume)
316+
state.stats_volume_cap_chars_max = max(state.stats_volume_cap_chars_max, len(str(vol_total)))
323317
vc_w = state.stats_volume_cap_chars_max
324318
archive_max_name = len(basename) + vc_w + 6
325319
archive_max_num = vc_w + 1
@@ -333,22 +327,10 @@ def incremental_stats_restore(
333327
mv_tail = STATS_INCREMENTAL_COL_GAP + mv_decor if mv_decor else ""
334328
timestamp = int(time.time())
335329
elapsed_time = timestamp - state.start_timestamp - state.start_timestamp_running
336-
pct = (tar_volume * 100) // volume_cap if volume_cap else 0
330+
pct = (tar_volume * 100) // vol_total if vol_total else 0
337331

338332
avg_time = elapsed_time // tar_volume if tar_volume > 0 else 0
339-
remain_time = avg_time * max(0, volume_cap - tar_volume)
340-
if (
341-
remain_time == 0
342-
and pct < 100
343-
and tar_volume > 0
344-
and avg_time > 0
345-
and state.source_size_total > 0
346-
and state.source_size_running < state.source_size_total
347-
):
348-
bpc = max(1, state.source_size_running // tar_volume)
349-
bytes_rem = state.source_size_total - state.source_size_running
350-
est_chunks = (bytes_rem + bpc - 1) // bpc
351-
remain_time = avg_time * est_chunks
333+
remain_time = avg_time * max(0, vol_total - tar_volume)
352334

353335
incremental_time = timestamp - state.incremental_timestamp - state.incremental_timestamp_running
354336
comp_ratio = (
@@ -362,14 +344,7 @@ def incremental_stats_restore(
362344

363345
el_txt = duration_readable(elapsed_time)
364346
elapsed_seg = "elapsed.." + el_txt
365-
if not state.stats_line_elapsed_seeded:
366-
seed_elapsed = "elapsed.." + duration_readable(remain_time)
367-
state.stats_line_elapsed_seg_w = max(
368-
state.stats_line_elapsed_seg_w,
369-
len(seed_elapsed),
370-
len(elapsed_seg),
371-
)
372-
state.stats_line_elapsed_seeded = True
347+
state.stats_line_elapsed_seg_w = max(state.stats_line_elapsed_seg_w, len(elapsed_seg))
373348

374349
inc_txt = duration_readable(incremental_time)
375350
state.incremental_text_size_running = max(state.incremental_text_size_running, len(inc_txt))
@@ -400,7 +375,7 @@ def incremental_stats_restore(
400375

401376
date_s = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp))
402377
prefix = (
403-
f"{filename:<{archive_max_name}s} {f'/{volume_cap}':>{archive_max_num}s} "
378+
f"{filename:<{archive_max_name}s} {f'/{vol_total}':>{archive_max_num}s} "
404379
f"{_stats_pct_field(pct)}{STATS_INCREMENTAL_COL_GAP}"
405380
)
406381
_emit_incremental_stats_full_line(

tests/test_stats.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,7 @@ def test_incremental_stats_restore_volume_one_shows_remain(capsys, monkeypatch)
143143
)
144144
statsmod.incremental_stats_restore("test", state, "test.000001.tar", 1)
145145
out = capsys.readouterr().out
146-
# Denominator uses byte extrapolation (≈20 chunks) rather than archive_volumes alone.
147-
assert "remain..31m:40s" in out
146+
assert "remain..15m" in out
148147

149148

150149
def test_incremental_stats_restore_tier3_suffix(capsys, monkeypatch) -> None:
@@ -458,8 +457,8 @@ def test_fmt_stats_compr_segment_fixed_width() -> None:
458457
assert len(statsmod._fmt_stats_compr_segment(8)) == len(statsmod._fmt_stats_compr_segment(42))
459458

460459

461-
def test_incremental_stats_elapsed_column_seeded_from_first_remain(capsys, tmp_path: Path, monkeypatch) -> None:
462-
"""First full line sets ``elapsed..`` width from that line's remain; no pre-run preseed."""
460+
def test_incremental_stats_elapsed_column_monotonic_width(capsys, tmp_path: Path, monkeypatch) -> None:
461+
"""``elapsed..`` column width grows with the longest ``elapsed..`` segment seen (never shrinks)."""
463462
dest = tmp_path / "dest"
464463
dest.mkdir()
465464
state = persistence.RuntimeState(
@@ -479,9 +478,8 @@ def test_incremental_stats_elapsed_column_seeded_from_first_remain(capsys, tmp_p
479478
i_el = out.index("elapsed..")
480479
i_last = out.index("last..")
481480
assert out[i_el - 2 : i_el] == gap, "remain.. cell → elapsed.. uses two-space gutter"
482-
# Elapsed column width matches first-line remain ceiling, not an oversized preseed.
483-
assert i_last - i_el == len("elapsed..2m:48s") + len(gap), (
484-
f"expected tight elapsed→last boundary in {out!r}, span {i_last - i_el}"
481+
assert i_last - i_el == len("elapsed..8s") + len(gap), (
482+
f"expected elapsed→last boundary from first elapsed segment in {out!r}, span {i_last - i_el}"
485483
)
486484
i_src = out.index("source..")
487485
assert out[i_src - 2 : i_src] == gap

0 commit comments

Comments
 (0)