NAME
Test::CPAN::Health::Cache - Persistent SQLite cache for network check results
SYNOPSIS
use Test::CPAN::Health::Cache;
my $cache = Test::CPAN::Health::Cache->new(
cache_dir => "$ENV{HOME}/.cache/cpan-health",
);
$cache->store('sem_ver:Foo-Bar:1.00', { status => 'pass', score => 100 });
my $data = $cache->get('sem_ver:Foo-Bar:1.00');
DESCRIPTION
A lightweight SQLite-backed key/value store that persists health check results between runs to avoid hammering rate-limited external APIs.
Each entry has a TTL (seconds); get returns undef and behaves as a cache miss for expired entries. TTLs are keyed by check id prefix against the %DEFAULT_TTLS table; unknown checks fall back to 24 hours.
The database is created automatically on first use under cache_dir.
LIMITATIONS
No row-level locking; concurrent processes writing the same key may race. SQLite's journal mode provides transaction safety but not write ordering.
Expired rows are pruned lazily on
getand on a scheduled sweep; disk usage may grow unboundedly ifpurgeis never called.
get
Retrieve a cached value by key. Returns undef on cache miss or if the entry has expired.
API SPECIFICATION
INPUT
key Scalar required opaque cache key (e.g. 'sem_ver:Foo-Bar:1.00')
OUTPUT
Hashref of the stored data, or undef on miss/expiry.
MESSAGES
Code | Severity | Message | Resolution
------+----------+------------------------------------+---------------------
CAC01 | WARNING | Cache read error: {msg} | Check DB permissions
FORMAL SPECIFICATION
-- Z schema (placeholder) --
GetOp
key? : String
value! : Hashref | undefined
now : Timestamp
-------------------------------------------------------
key? /= ""
value! /= undefined <=> exists entry(key?) /\ entry(key?).expires > now
SIDE EFFECTS
May delete expired rows from the SQLite database.
USAGE EXAMPLE
my $data = $cache->get('security_advisories:Foo-Bar:1.00');
store
PURPOSE
Store a value in the cache under the given key. The TTL is determined automatically from the check id embedded in the key (first colon-delimited segment) using the %DEFAULT_TTLS table.
API SPECIFICATION
INPUT
key Scalar required
value Hashref required
ttl Scalar optional override TTL in seconds
OUTPUT
Returns $self.
MESSAGES
Code | Severity | Message | Resolution
------+----------+------------------------------------+---------------------
CAC02 | WARNING | Cache write error: {msg} | Check DB permissions/disk space
FORMAL SPECIFICATION
-- Z schema (placeholder) --
SetOp
Cache
Cache'
key? : String
value? : Hashref
ttl? : N | undefined
now : Timestamp
-------------------------------------------------------
Cache'.entries = Cache.entries (+) {key? |-> {value: value?, expires: now + ttl?}}
SIDE EFFECTS
Writes to the SQLite database.
USAGE EXAMPLE
$cache->store('sem_ver:Foo-Bar:1.00', { status => 'pass', score => 100 });
record_history
PURPOSE
Record an overall health score for a distribution version. Used by the Runner to build a score-over-time trend.
API SPECIFICATION
INPUT
dist Scalar required Distribution name (e.g. 'LWP-UserAgent')
version Scalar required Version string
score Scalar required Integer score 0..100
OUTPUT
Returns $self.
MESSAGES
Code | Severity | Message | Resolution
------+----------+------------------------------------+---------------------
CAC04 | WARNING | History write error: {msg} | Check DB permissions
FORMAL SPECIFICATION
Post: score_history table gains one row (dist, version, score, time())
SIDE EFFECTS
Writes to the SQLite database.
USAGE EXAMPLE
$cache->record_history('LWP-UserAgent', '6.77', 95);
score_history
PURPOSE
Retrieve recent score history for a distribution in reverse-chronological order.
API SPECIFICATION
INPUT
dist Scalar required Distribution name
limit Integer optional Maximum rows to return (default: 10)
OUTPUT
Arrayref of hashrefs with keys dist, version, score, recorded (Unix timestamp). Most-recent first.
MESSAGES
Code | Severity | Message | Resolution
------+----------+------------------------------------+---------------------
CAC05 | WARNING | History read error: {msg} | Check DB permissions
FORMAL SPECIFICATION
Post: result is an arrayref of at most limit rows for dist, ordered by recorded DESC
SIDE EFFECTS
Reads from the SQLite database.
USAGE EXAMPLE
for my $row (@{ $cache->score_history('LWP-UserAgent') }) {
printf "%s: %d\n", scalar localtime($row->{recorded}), $row->{score};
}
purge
PURPOSE
Delete all expired entries from the cache database. Intended to be called periodically (e.g. at CLI startup) to reclaim disk space.
API SPECIFICATION
INPUT
None.
OUTPUT
Integer: number of rows deleted.
MESSAGES
Code | Severity | Message | Resolution
------+----------+------------------------------------+---------------------
CAC03 | WARNING | Cache purge error: {msg} | Check DB permissions
FORMAL SPECIFICATION
-- Z schema (placeholder) --
PurgeOp
Cache
Cache'
now : Timestamp
deleted : N
-------------------------------------------------------
Cache'.entries = {e : Cache.entries | e.expires > now}
deleted = #Cache.entries - #Cache'.entries
SIDE EFFECTS
Deletes rows from the SQLite database.
USAGE EXAMPLE
printf "Purged %d stale cache entries\n", $cache->purge;
AUTHOR
Nigel Horne, <njh at nigelhorne.com>
LICENSE AND COPYRIGHT
Copyright (C) 2026 Nigel Horne.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.