Skip to content

feat(#55): configurable cache retention via PULLMD_CACHE_RETENTION_DAYS - #56

Merged
syswave-dev merged 3 commits into
mainfrom
feat/cache-retention-55
Sep 3, 2026
Merged

syswave-dev merged 3 commits into
mainfrom
feat/cache-retention-55

Conversation

@syswave-dev

Copy link
Copy Markdown
Collaborator

Closes #55.

The 90-day cache retention was hardcoded in four places in lib/cache.js (share lookup, prune, expiring-soon window, reported retentionDays) plus one fallback literal in server.js. It is now read from a single env var.

PULLMD_CACHE_RETENTION_DAYS

  • Non-negative integer number of days, accepted range 0 to 36500. Default 90, so existing instances behave exactly as before.
  • 0 = unlimited: no prune statement is prepared, /s/:id lookups carry no age condition, expiringSoon is 0 without a query. The cache doubles as an archive.
  • Unset or empty is silently 90 (the compose files pass ${VAR:-}, so empty is the normal unconfigured case). Anything else warns once at startup and falls back to 90; the server never refuses to start over this value.
  • expiringSoon follows the setting: rows within 10 days of being pruned (max(retention - 10, 0)), matching the old 80-of-90 window at the default.
  • GET /api/storage reports the effective value; the PWA footer renders "unbegrenzt" / "unlimited" for 0.

Wiring

  • createCache(dbPath, { retentionDays }) validates the option before opening the database, since the prepared statements bake the window into their SQL. The env parser readCacheRetentionDays(env, warn) is the only coercion boundary and is unit-tested in isolation.
  • server.js reads the variable once at startup and passes it to both the cache and createApp, so a malformed value warns exactly once and both consumers agree.
  • Both bundled compose files enumerate environment variables explicitly, so they now pass the new one through. Without that line it never reaches the container.

Docs

.env.example, README (config table, Cache & TTLs, architecture list), help.html DE+EN (three pairs), CHANGELOG. Session-cookie TTL mentions of "90 days" are a different feature and untouched.

Caveat documented in .env.example and README: lowering the value on a running instance prunes every row older than the new value on the next cache write.

Tests

1264 baseline plus new tests for the parser (unset/empty silent, trimming, 0, malformed and out-of-range values), the cache option (share lookup, prune + orphan sweep, expiring-soon clamp, unlimited, constructor rejection) and /api/storage with and without a cache.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TFVGVrGAFP1TuYVGGmEoHK

syswave-dev and others added 3 commits September 3, 2026 11:17
The 90-day window was hardcoded in lib/cache.js (share lookup, prune, expiring-soon stat) plus one fallback literal in server.js. It is now read from PULLMD_CACHE_RETENTION_DAYS: a non-negative integer, default 90, where 0 means unlimited (no prune, no age condition on /s/:id, expiringSoon 0). Unset or empty falls back silently, anything malformed warns once and falls back. The PWA footer renders unbegrenzt/unlimited for 0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TFVGVrGAFP1TuYVGGmEoHK
…ompose

Documents the configurable cache retention shipped in c6c0e76: the env var block in .env.example, a configuration-table row plus the Cache & TTLs and architecture lines in README.md, the bilingual TTL table and share-link bullets in public/help.html, and an Unreleased/Added entry in CHANGELOG.md. Both compose files enumerate environment variables explicitly, so they now pass PULLMD_CACHE_RETENTION_DAYS through; without that line the variable never reaches the container. Every remaining 90 in these files now reads as a default rather than a fixed value, and the session-cookie 90-day lines are untouched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TFVGVrGAFP1TuYVGGmEoHK
readCacheRetentionDays accepted any digit string, so an absurd value went
through unchecked. A value like 9999999 produced datetime('now', '-9999999
days'), which is outside SQLite's date range and evaluates to NULL: every
/s/:id lookup silently 404'd and expiringSoon read 0. A 22-digit value became
1e+22, an integer as far as Number.isInteger is concerned, whose interpolated
modifier is not valid SQLite syntax at all, with the same silent outcome. A
309-digit value parsed to Infinity, failed the Number.isInteger check inside
createCache and crashed the server at startup, which the "never crash on a bad
env value" rule forbids.

Both the parser and createCache now reject anything above the new
MAX_CACHE_RETENTION_DAYS (36500, 100 years): the parser warns once and falls
back to 90, the constructor throws. .env.example, the README row and the
CHANGELOG entry name the accepted range and spell out that unset or empty
still means 90 without a warning.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TFVGVrGAFP1TuYVGGmEoHK
@syswave-dev
syswave-dev merged commit 96aa004 into main Sep 3, 2026
4 checks passed
@syswave-dev
syswave-dev deleted the feat/cache-retention-55 branch September 3, 2026 09:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make cache retention configurable (CACHE_RETENTION_DAYS)

1 participant