aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-28 03:53:24 -0400
committerCraig Jennings <c@cjennings.net>2026-06-28 03:53:24 -0400
commitec38418d6d3eb4342363af1714f0073a74d52fbc (patch)
tree1327072f95de36f0577e114fd259c8d064426619
parent944a52f0e33087dafba383edb3d97ec19a06d361 (diff)
downloademacs-wttrin-ec38418d6d3eb4342363af1714f0073a74d52fbc.tar.gz
emacs-wttrin-ec38418d6d3eb4342363af1714f0073a74d52fbc.zip
fix: always evict at least one cache entry when over max
wttrin--cleanup-cache-if-needed removed floor(count * 0.20) entries, which is 0 for a small cache (e.g. two entries at max one), so the cache could sit over its advertised maximum indefinitely. It now removes at least one whenever it is over.
-rw-r--r--tests/test-wttrin--cleanup-cache-if-needed.el9
-rw-r--r--wttrin.el7
2 files changed, 9 insertions, 7 deletions
diff --git a/tests/test-wttrin--cleanup-cache-if-needed.el b/tests/test-wttrin--cleanup-cache-if-needed.el
index 1850d17..7533bfa 100644
--- a/tests/test-wttrin--cleanup-cache-if-needed.el
+++ b/tests/test-wttrin--cleanup-cache-if-needed.el
@@ -108,16 +108,17 @@
(should (= 1 (testutil-wttrin-cache-size))))
(test-wttrin--cleanup-cache-if-needed-teardown)))
-(ert-deftest test-wttrin--cleanup-cache-if-needed-boundary-two-entries-at-max-one-removes-none ()
- "Test that two entries at max=1 removes no entries due to integer division."
+(ert-deftest test-wttrin--cleanup-cache-if-needed-boundary-two-entries-at-max-one-reduces-to-max ()
+ "Boundary: two entries at max=1 reduces to the max.
+floor(2 * 0.20) is 0, but cleanup must still remove at least one entry when the
+cache is over max, or it would exceed its advertised maximum indefinitely."
(test-wttrin--cleanup-cache-if-needed-setup)
(unwind-protect
(testutil-wttrin-with-cache-max 1
(testutil-wttrin-add-to-cache "old" "data1" 100)
(testutil-wttrin-add-to-cache "new" "data2" 50)
(wttrin--cleanup-cache-if-needed)
- ;; 2 entries / 5 = 0 in integer division, so no entries removed
- (should (= 2 (testutil-wttrin-cache-size))))
+ (should (= 1 (testutil-wttrin-cache-size))))
(test-wttrin--cleanup-cache-if-needed-teardown)))
(ert-deftest test-wttrin--cleanup-cache-if-needed-boundary-one-over-max-removes-oldest ()
diff --git a/wttrin.el b/wttrin.el
index cdef10a..4bc1b3a 100644
--- a/wttrin.el
+++ b/wttrin.el
@@ -1283,12 +1283,13 @@ Returns a list where each element is a cons cell (key . timestamp)."
(defun wttrin--cleanup-cache-if-needed ()
"Remove oldest entries if cache exceeds max size.
Removes oldest entries based on `wttrin--cache-cleanup-percentage'
-when cache count exceeds `wttrin-cache-max-entries'.
+when cache count exceeds `wttrin-cache-max-entries', and always at least one
+so the cache can't sit over its maximum when the percentage floors to zero.
This creates headroom to avoid frequent cleanups."
(when (> (hash-table-count wttrin--cache) wttrin-cache-max-entries)
(let* ((entries-by-age (wttrin--get-cache-entries-by-age))
- (num-to-remove (floor (* (length entries-by-age)
- wttrin--cache-cleanup-percentage))))
+ (num-to-remove (max 1 (floor (* (length entries-by-age)
+ wttrin--cache-cleanup-percentage)))))
(dotimes (i num-to-remove)
(remhash (car (nth i entries-by-age)) wttrin--cache)))))