mirror of
https://github.com/rustfs/rustfs.git
synced 2026-09-08 13:06:00 +00:00
fix(scanner): consume raw page owner resume oracle (#7391)
Treat persisted raw page owner entries as a validated set, not a read_dir-order prefix, so restart scans can consume committed owner pages without recounting them against the raw enumeration budget. Commit checkpointed partial pages, validate owner parent/generation/digests before the skip oracle, and fail closed on duplicate/corrupt page state. Extend the real scanner restart driver to enforce fixed raw-entry and object budgets across fresh OS processes, and report owner-index coverage in each worker round. Co-authored-by: zhi22915 <qiuzgang@gmail.com>
This commit is contained in:
@@ -30,15 +30,21 @@ def validate_report(report, *, round_number, pid, objects, budget):
|
||||
if type(report.get(key)) is not int or report[key] != value:
|
||||
raise ValueError(f"worker report mismatch: {key}")
|
||||
for key in ("raw_entries", "raw_name_bytes", "objects_before", "objects_retained",
|
||||
"versions_retained", "bytes_retained", "objects_processed"):
|
||||
"versions_retained", "bytes_retained", "objects_processed",
|
||||
"raw_page_index_committed_entries", "raw_page_index_indexed_entries"):
|
||||
if type(report.get(key)) is not int or not 0 <= report[key] <= 1048576:
|
||||
raise ValueError(f"invalid bounded counter: {key}")
|
||||
if report["raw_entries"] == 0:
|
||||
made_budgeted_object_progress = report["objects_processed"] > 0
|
||||
if report["raw_entries"] == 0 and not made_budgeted_object_progress:
|
||||
raise ValueError("nonempty fixture must observe raw entries; budget hook may not have run")
|
||||
if report["raw_entries"] > budget:
|
||||
raise ValueError("raw-entry budget exceeded; no unbudgeted tail is permitted")
|
||||
if report["objects_processed"] > budget:
|
||||
raise ValueError("object budget exceeded; no unbudgeted scan tail is permitted")
|
||||
for key in ("raw_first_entry", "raw_last_entry"):
|
||||
value = report.get(key)
|
||||
if report["raw_entries"] == 0 and made_budgeted_object_progress and value is None:
|
||||
continue
|
||||
if type(value) is not str or not 0 < len(value.encode("utf-8")) <= 512:
|
||||
raise ValueError(f"invalid raw entry marker: {key}")
|
||||
if type(report.get("snapshot_complete")) is not bool:
|
||||
|
||||
@@ -10,6 +10,8 @@ class ReportTests(unittest.TestCase):
|
||||
return dict(schema=1, round=0, pid=123, objects_expected=4, raw_entry_budget=16,
|
||||
raw_entries=8, raw_name_bytes=64, objects_before=0, objects_retained=4,
|
||||
versions_retained=4, bytes_retained=4, objects_processed=4,
|
||||
raw_page_index_committed_entries=4,
|
||||
raw_page_index_indexed_entries=4,
|
||||
raw_first_entry="bucket/object-0000",
|
||||
raw_last_entry="bucket/object-0003/xl.meta",
|
||||
snapshot_complete=True, outcome="complete")
|
||||
@@ -44,6 +46,11 @@ class ReportTests(unittest.TestCase):
|
||||
with self.assertRaises(ValueError):
|
||||
self.validate(report)
|
||||
|
||||
report = self.report()
|
||||
report["objects_processed"] = 17
|
||||
with self.assertRaises(ValueError):
|
||||
self.validate(report)
|
||||
|
||||
def test_missing_or_oversized_raw_marker_rejected(self):
|
||||
for value in (None, True, "", "x" * 513):
|
||||
with self.subTest(value=value):
|
||||
@@ -55,9 +62,19 @@ class ReportTests(unittest.TestCase):
|
||||
def test_complete_coverage_without_entry_observation_rejected(self):
|
||||
report = self.report()
|
||||
report["raw_entries"] = 0
|
||||
report["objects_processed"] = 0
|
||||
report["raw_page_index_committed_entries"] = 3
|
||||
with self.assertRaises(ValueError):
|
||||
self.validate(report)
|
||||
|
||||
def test_budgeted_object_progress_can_consume_all_raw_entries(self):
|
||||
report = self.report()
|
||||
report["raw_entries"] = 0
|
||||
report["raw_first_entry"] = None
|
||||
report["raw_last_entry"] = None
|
||||
report["objects_processed"] = 1
|
||||
self.validate(report)
|
||||
|
||||
def test_missing_wrong_type_and_negative_counter_rejected(self):
|
||||
for value in (None, True, -1, "8", 1048577):
|
||||
with self.subTest(value=value):
|
||||
|
||||
Reference in New Issue
Block a user