Cache expiration and invalidation
DataCaches provides first-class support for cache invalidation: time-to-live (TTL), stale detection, bulk invalidation, fine-grained purging, and automatic post-write purge policies.
TTL and staleness
Per-entry TTL
Pass a ttl keyword to write! to record a time-to-live for an entry. The value must be a Dates.Period (e.g. Dates.Hour(6), Dates.Day(30)).
using DataCaches, Dates
dc = DataCache(:myproject)
# Cache a result with a 6-hour TTL
write!(dc, result; label = "query1", ttl = Hour(6))
# Check staleness before re-using
if isstale(dc, "query1")
result = fetch_live()
write!(dc, result; label = "query1", ttl = Hour(6))
end
data = read(dc, "query1")read does not enforce staleness — stale entries are readable until explicitly removed. This enables a stale-while-revalidate pattern: serve from the cache while refreshing in the background.
Cache-level default TTL
Set a default TTL on the cache itself so every write! inherits it automatically:
# All writes to this cache expire after 24 hours unless overridden per-entry
dc = DataCache(:myproject; default_ttl = Day(1))
write!(dc, result; label = "query1") # inherits Day(1)
write!(dc, result; label = "query2", ttl = Hour(1)) # overrides to Hour(1)The default_ttl is persisted in the cache's TOML index, so it is remembered when the cache is reloaded in a future session.
isstale
isstale checks whether an entry has exceeded its TTL:
isstale(dc, "query1") # by label
isstale(dc, entry) # by CacheEntry
isstale("query1") # uses active_autocache()
isstale(entry) # uses active_autocache()Returns false when no TTL applies (neither per-entry nor cache default). Legacy entries whose datecached is unknown are never considered stale.
Stale-while-revalidate pattern
function get_data(dc, label, fetch_fn; ttl = Hour(6))
# Return cached value even if stale; trigger background refresh when stale
if haskey(dc, label) && isstale(dc, label)
@async begin
result = fetch_fn()
write!(dc, result; label = label, ttl = ttl)
end
elseif !haskey(dc, label)
result = fetch_fn()
write!(dc, result; label = label, ttl = ttl)
end
return read(dc, label)
endinvalidate! — bulk invalidation
invalidate! removes entries matching a set of criteria in a single batched index rewrite. When called without a cache argument, uses active_autocache().
By staleness
# Remove all entries that have exceeded their TTL
invalidate!(dc; stale = true)
invalidate!(; stale = true) # default cacheBy label pattern
# Remove entries whose label/description matches a pattern
invalidate!(dc; pattern = r"^temp_")
invalidate!(dc; pattern = "old_run_") # String is converted to RegexBy date
# Remove entries cached more than 30 days ago
invalidate!(dc; before = now() - Day(30))
# Remove entries from a specific window
invalidate!(dc; after = DateTime("2026-01-01T00:00:00"),
before = DateTime("2026-02-01T00:00:00"))By file format
# Remove all JLS (binary serialization) entries
invalidate!(dc; format = "jls")
invalidate!(dc; format = r"jls|json") # multiple formats via RegexBy predicate function
A predicate function receives a CacheEntry and returns true if the entry should be removed:
# Remove entries whose description starts with a particular function name
invalidate!(dc; predicate = e -> startswith(e.description, "my_func("))
# Remove large files
invalidate!(dc; predicate = e -> isfile(e.path) && stat(e.path).size > 50_000_000)Combining criteria
All filter kwargs (same as entries / CacheAssets.ls) are accepted first to scope the candidate set, then stale and predicate are applied as additional restrictions:
# Remove stale, labeled-only entries cached before a date
invalidate!(dc; labeled = true, before = now() - Day(7), stale = true)Dry-run preview
invalidate!(dc; stale = true, dry_run = true)
# Prints: "invalidate! (dry run): N entries would be removed: …"
# No entries are deleted.CacheAssets.purge! — power tool
DataCaches.CacheAssets.purge! is the full-featured purge function with LRU eviction, size limits, and all the filter kwargs accepted by CacheAssets.ls.
Remove by age
using DataCaches.CacheAssets
# Remove entries older than 30 days
purge!(dc; max_age = Day(30))Remove by idle time (LRU)
# Remove entries not accessed in the last 7 days
purge!(dc; max_idle = Day(7))max_idle requires track_access = true (the default) on the cache. Entries that have never been accessed are treated as most-idle.
Keep only the N most recently accessed
# Keep the 20 most recently accessed entries; delete the rest
purge!(dc; keep_count = 20)Limit total cache size
# Purge LRU entries until total cache size is under 500 MiB
purge!(dc; max_size_bytes = 500 * 1024 * 1024)Protect labeled entries
# Purge only unlabeled entries older than a week; leave labeled entries alone
purge!(dc; max_age = Day(7), keep_labeled = true)Combine with format or pattern filters
# Delete JLS entries not accessed in the last 30 days
purge!(dc; format = "jls", max_idle = Day(30))
# Delete entries matching a label pattern that are older than a month
purge!(dc; pattern = r"^temp_", max_age = Day(30))Dry-run
purge!(dc; max_age = Day(7), dry_run = true)
# Prints: "purge! (dry run): N entries would be removed: …"
# No entries are deleted.Remove all stale entries via purge!
purge!(dc; stale = true)Auto-purge policies
set_autopurge! attaches a PurgePolicy to a cache. The policy runs automatically at the end of every write!, keeping the cache within configured limits without manual intervention.
LRU eviction
# Keep only the 50 most recently accessed entries
set_autopurge!(dc; keep_count = 50)
# Write a new result — oldest entries are pruned automatically
write!(dc, new_result; label = "latest")Age-based cleanup
# Entries older than 14 days are automatically removed on each write
set_autopurge!(dc; max_age = Day(14))Size-based cleanup
# Cache stays under 1 GiB; LRU entries are pruned when the limit is exceeded
set_autopurge!(dc; max_size_bytes = 1024^3)Combined policy
# Remove entries older than 30 days OR keep only the last 100,
# but never delete labeled entries
set_autopurge!(dc; max_age = Day(30), keep_count = 100, keep_labeled = true)Disabling auto-purge
set_autopurge!(dc; enabled = false)Using the autocache store
All functions accept a zero-argument form that operates on active_autocache():
set_autopurge!(; keep_count = 20)
invalidate!(; stale = true)
CacheAssets.purge!(; max_age = Day(30))Quick-reference table
| Function | Description |
|---|---|
write!(dc, data; ttl = Hour(6)) | Write with per-entry TTL |
DataCache(store; default_ttl = Day(1)) | Cache-level default TTL |
isstale(dc, label) / isstale(entry) | Check if entry is past TTL |
invalidate!(dc; stale, pattern, format, before, after, labeled, predicate, dry_run) | Bulk removal by criteria |
CacheAssets.purge!(dc; max_age, max_idle, keep_count, max_size_bytes, stale, keep_labeled, dry_run, …ls filters…) | Power tool with LRU/size limits |
set_autopurge!(dc; max_age, max_idle, keep_count, max_size_bytes, keep_labeled, enabled) | Configure automatic post-write purging |
API reference
DataCaches.isstale — Function
isstale(cache::DataCache, entry::CacheEntry) → Bool
isstale(cache::DataCache, label::AbstractString) → Bool
isstale(entry::CacheEntry) → Bool
isstale(label::AbstractString) → BoolReturn true if entry (or the entry identified by label) has exceeded its time-to-live and is considered stale.
The effective TTL for an entry is its own entry.ttl if set, otherwise the cache's default_ttl. When no TTL applies (both are nothing), returns false.
Staleness is purely informational — read does not enforce it. The caller decides whether to refresh (enabling stale-while-revalidate patterns).
Examples
dc = DataCache(:myproject; default_ttl = Dates.Hour(1))
write!(dc, result; label = "query1")
isstale(dc, "query1") # false immediately after write
# … one hour later …
isstale(dc, "query1") # true
# Per-entry TTL overrides the cache default:
write!(dc, result2; label = "short", ttl = Dates.Minute(5))
isstale(dc, "short") # true after 5 minutesSee also: invalidate!, DataCaches.CacheAssets.purge!.
DataCaches.invalidate! — Function
invalidate!(cache::DataCache; kwargs...) → DataCache
invalidate!(; kwargs...) → DataCacheRemove entries from cache that match the given criteria. All removals are batched into a single index rewrite.
When called without a cache argument, uses active_autocache().
Filtering keyword arguments
Entries are first selected by the standard filter set (same as entries):
pattern—RegexorString; matched against label, description, or UUIDfilepath_pattern—RegexorString; matched against full file pathfilename_pattern—RegexorString; matched against filename onlyformat—StringorRegex; matched against the entry's format tag ("csv","jls", …)before::DateTime— entries cached before this timeafter::DateTime— entries cached after this timeaccessed_before_date::DateTime— entries last accessed before this timeaccessed_after_date::DateTime— entries last accessed after this timelabeled::Union{Nothing,Bool}—true= labeled only;false= unlabeled only
Invalidation criteria
stale::Bool = false— further restrict to entries that are past their TTLpredicate::Union{Nothing,Function} = nothing— further restrict to entries wherepredicate(entry)returnstrue
Options
dry_run::Bool = false— print matching entries toiowithout deletingio::IO = stdout— output stream fordry_runmessages
Examples
dc = DataCache(:myproject; default_ttl = Dates.Hour(24))
# Remove all stale entries
invalidate!(dc; stale = true)
# Remove entries matching a label pattern
invalidate!(dc; pattern = r"^temp_")
# Remove CSV entries older than a week
invalidate!(dc; format = "csv", before = Dates.now() - Dates.Day(7))
# Remove entries matching a custom predicate
invalidate!(dc; predicate = e -> startswith(e.description, "my_func("))
# Preview what would be removed
invalidate!(dc; stale = true, dry_run = true)See also: isstale, DataCaches.CacheAssets.purge!.
DataCaches.set_autopurge! — Function
set_autopurge!(cache::DataCache; enabled=true, kwargs...) → DataCache
set_autopurge!(; enabled=true, kwargs...) → DataCacheConfigure an automatic purge policy on cache. After each write!, the policy is applied to keep the cache within the specified limits.
When called without a cache argument, uses active_autocache().
Pass enabled = false to remove the current policy (disabling auto-purge).
Keyword arguments
max_age::Union{Nothing,Period}— remove entries older than this age (bydatecached)max_idle::Union{Nothing,Period}— remove entries not accessed within this periodkeep_count::Union{Nothing,Int}— keep only the N most-recently-accessed entriesmax_size_bytes::Union{Nothing,Int}— purge LRU entries until total cache size ≤ limitkeep_labeled::Bool = false— whentrue, labeled entries are never auto-purged
Examples
dc = DataCache(:myproject)
# Keep only the 20 most recently accessed entries
set_autopurge!(dc; keep_count = 20)
# Remove entries older than 30 days, never delete labeled entries
set_autopurge!(dc; max_age = Dates.Day(30), keep_labeled = true)
# Disable auto-purge
set_autopurge!(dc; enabled = false)See also: DataCaches.CacheAssets.purge!, invalidate!.
DataCaches.CacheAssets.purge! — Function
DataCaches.CacheAssets.purge!([cache::DataCache]; kwargs...) → DataCacheBulk-delete cache entries matching the given criteria. All standard ls filtering keyword arguments are accepted to scope which entries are candidates for purging. When cache is omitted, the active default cache is used (see active_autocache()).
All removals are batched into a single index rewrite.
Standard filter kwargs (same as ls)
pattern,filepath_pattern,filename_pattern— label/path/filename patternformat::Union{Nothing,String,Regex}— format tag filter (e.g."jls",r"csv|json")before,after— filter bydatecachedaccessed_before_date,accessed_after_date— filter bydateaccessedlabeled::Union{Nothing,Bool}—true= labeled only;false= unlabeled only
Purge criteria (applied after filtering)
stale::Bool = false— delete entries past their TTL (requires TTL to be configured)max_age::Union{Nothing,Period}— delete entries whose age (bydatecached) exceeds thismax_idle::Union{Nothing,Period}— delete entries that have not been accessed within this period (entries never accessed are treated as least-recently-used and are eligible)keep_count::Union{Nothing,Int}— keep only the N most-recently-accessed entries among the candidates; delete the restmax_size_bytes::Union{Nothing,Int}— purge LRU entries until the total size of the remaining candidates is at or below this limit
Options
keep_labeled::Bool = false— whentrue, labeled entries are never deleted, even if they match all other criteriadry_run::Bool = false— print what would be deleted toiowithout deletingio::IO = stdout— output stream fordry_runmessages
Examples
dc = DataCache(:myproject)
# Remove all stale entries (requires TTL on entries or cache)
CacheAssets.purge!(dc; stale = true)
# Remove entries older than 30 days
CacheAssets.purge!(dc; max_age = Dates.Day(30))
# Keep only the 10 most recently accessed entries
CacheAssets.purge!(dc; keep_count = 10)
# Purge LRU entries until cache is under 100 MiB, keep labeled entries
CacheAssets.purge!(dc; max_size_bytes = 100 * 1024 * 1024, keep_labeled = true)
# Preview without deleting
CacheAssets.purge!(dc; max_age = Dates.Day(7), dry_run = true)
# Purge using default cache
CacheAssets.purge!(; keep_count = 50)See also ls, rm, DataCaches.invalidate!, DataCaches.set_autopurge!.