fix overzealous snapshot removal bug

This commit is contained in:
Dr. Matthias Ratajczak
2022-05-04 11:26:39 +02:00
parent 07960781b5
commit 21b3cb588e
2 changed files with 24 additions and 7 deletions
+14 -1
View File
@@ -207,7 +207,20 @@ class TestDataset:
counter = 0 counter = 0
ds._clean_old_snapshots_for_interval("daily", MAX_COUNT) ds._clean_old_snapshots_for_interval("daily", MAX_COUNT)
assert counter == MAX_COUNT assert counter <= MAX_COUNT
@pytest.mark.parametrize("max_count", [10, 40])
def test__get_oldest_snapshots_exceeding_max_count(self, max_count):
snapshots = [
snap for snap in gen_snapshots_for_tank_ROOT() if "daily" in str(snap)
]
ORIGINAL_COUNT = 31
assert len(snapshots) == ORIGINAL_COUNT
expected_result_len = ORIGINAL_COUNT - max_count
if expected_result_len < 0:
expected_result_len = 0
result = Dataset._get_oldest_snapshots_exceeding_max_count(snapshots, max_count)
assert len(result) == expected_result_len
def gen_snapshots_for_tank_ROOT() -> list[Snapshot]: def gen_snapshots_for_tank_ROOT() -> list[Snapshot]:
+10 -6
View File
@@ -205,6 +205,7 @@ class ZFSPath:
"""Return the path in the same manner as `zfs`.""" """Return the path in the same manner as `zfs`."""
path = "/".join(self._elements) path = "/".join(self._elements)
if self._is_snapshot: if self._is_snapshot:
assert self._snapshot_name is not None
return "@".join([path, self._snapshot_name]) return "@".join([path, self._snapshot_name])
return path return path
@@ -722,22 +723,25 @@ class Dataset:
for interval, number in config.keep_snapshots_per_interval.items(): for interval, number in config.keep_snapshots_per_interval.items():
self._clean_old_snapshots_for_interval(interval, number) self._clean_old_snapshots_for_interval(interval, number)
def _clean_old_snapshots_for_interval( def _clean_old_snapshots_for_interval(self, interval: str, max_count: int) -> None:
self, interval: str, max_count: int
) -> None: # TODO I assume the regex matches across different datasets, which it shouldn't
regex = _get_regex_matching_snapshots_with_tags([config.snapshot_tag, interval]) regex = _get_regex_matching_snapshots_with_tags([config.snapshot_tag, interval])
snapshots = [ snapshots = [
snapshot for snapshot in self.snapshots if snapshot.matches_regex(regex) snapshot for snapshot in self.snapshots if snapshot.matches_regex(regex)
] ]
old_snapshots = self._get_n_oldest_snapshots(snapshots, max_count) old_snapshots = self._get_oldest_snapshots_exceeding_max_count(
snapshots, max_count
)
for snapshot in old_snapshots: for snapshot in old_snapshots:
snapshot.destroy() snapshot.destroy()
@staticmethod @staticmethod
def _get_n_oldest_snapshots( def _get_oldest_snapshots_exceeding_max_count(
snapshots: list[Snapshot], count: int snapshots: list[Snapshot], max_count: int
) -> list[Snapshot]: ) -> list[Snapshot]:
snapshots.sort() snapshots.sort()
count = len(snapshots) - max_count
if count < 0:
count = 0
return snapshots[:count] return snapshots[:count]