fix: clean up stale DB recording entries when file is missing on disk

When reduce_storage_consumption() encountered a FileNotFoundError
(file deleted outside Frigate), it silently skipped the recording
without removing it from the database. Over time this caused the DB
to accumulate stale entries, making "Frigate recordings tracked" in
/system#storage dramatically overstate actual disk usage.

The bug also affected cleanup behaviour: stale entries don't count
toward freed-space accounting, so Phase 2 (force-delete retained
recordings) could trigger prematurely when most old entries were stale.

Fix: always append the recording to deleted_recordings regardless of
whether the file existed, so the DB entry is removed. freed-space
accounting is unchanged — FileNotFoundError still does not increment
deleted_segments_size since no actual disk space was recovered.

Applied to both Phase 1 (non-retained) and Phase 2 (retained) loops
inside reduce_storage_consumption().

https://claude.ai/code/session_01DMdSSQhQfTuXmzPtRvJmLB
This commit is contained in:
Claude 2026-03-16 04:58:47 +00:00
parent 52f78c9de9
commit f528a16065
No known key found for this signature in database

View File

@ -304,11 +304,12 @@ class StorageMaintainer(threading.Thread):
if not keep: if not keep:
try: try:
clear_and_unlink(Path(recording.path), missing_ok=False) clear_and_unlink(Path(recording.path), missing_ok=False)
deleted_recordings.append(recording)
deleted_segments_size += recording.segment_size deleted_segments_size += recording.segment_size
except FileNotFoundError: except FileNotFoundError:
# this file was not found so we must assume no space was cleaned up # File is missing from disk but the DB entry is stale; remove it
# without counting freed space since nothing was actually freed.
pass pass
deleted_recordings.append(recording)
# check if need to delete retained segments # check if need to delete retained segments
if deleted_segments_size < hourly_bandwidth: if deleted_segments_size < hourly_bandwidth:
@ -337,10 +338,11 @@ class StorageMaintainer(threading.Thread):
try: try:
clear_and_unlink(Path(recording.path), missing_ok=False) clear_and_unlink(Path(recording.path), missing_ok=False)
deleted_segments_size += recording.segment_size deleted_segments_size += recording.segment_size
deleted_recordings.append(recording)
except FileNotFoundError: except FileNotFoundError:
# this file was not found so we must assume no space was cleaned up # File is missing from disk but the DB entry is stale; remove it
# without counting freed space since nothing was actually freed.
pass pass
deleted_recordings.append(recording)
else: else:
logger.info(f"Cleaned up {deleted_segments_size} MB of recordings") logger.info(f"Cleaned up {deleted_segments_size} MB of recordings")