fix: retier never deletes an intact plain roll in favor of a corrupt .gz twin
the beyond-keep delete branch had no _gz_intact guard: when an intact plain file and its CORRUPT .gz twin (same mtime) straddled the keep boundary and listdir yielded the .gz first, the .gz sorted into the kept range while the intact plain sorted past it and was deleted - leaving the corrupt archive as the sole copy (permanent data loss), violating the 'corrupt .gz never wins over intact plain' invariant the two dedupe sites already enforce. the delete now skips an intact plain whose only surviving twin is a corrupt .gz; a later retier retires it once a clean .gz exists. reproduced with a forced .gz-first listdir: old deleted the plain, new keeps it. Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
@@ -288,8 +288,14 @@ def retier(log_dir: str, stem: str, keep_uncompressed: int, keep_compressed: int
|
|||||||
files.sort(key=lambda t: (t[1], t[2]), reverse=True)
|
files.sort(key=lambda t: (t[1], t[2]), reverse=True)
|
||||||
|
|
||||||
keep = keep_uncompressed + keep_compressed
|
keep = keep_uncompressed + keep_compressed
|
||||||
|
present = {p for p, _, _ in files}
|
||||||
for index, (path, _, _) in enumerate(files):
|
for index, (path, _, _) in enumerate(files):
|
||||||
if index >= keep:
|
if index >= keep:
|
||||||
|
# never delete an intact plain whose only surviving twin is a CORRUPT .gz -
|
||||||
|
# that would leave the corrupt archive as the sole copy (data loss). keep the
|
||||||
|
# plain; a later retier retires it once a clean .gz exists.
|
||||||
|
if not path.endswith(".gz") and (path + ".gz") in present and not _gz_intact(path + ".gz"):
|
||||||
|
continue
|
||||||
try:
|
try:
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
except OSError:
|
except OSError:
|
||||||
|
|||||||
Reference in New Issue
Block a user