Skip to content

Commit bfa6a94

Browse files
authored
add expire cache entries (#120)
1 parent 4b6e573 commit bfa6a94

File tree

3 files changed

+65
-1
lines changed

3 files changed

+65
-1
lines changed

cacholote/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@
1717

1818
from . import config, database, extra_encoders, utils
1919
from .cache import cacheable
20-
from .clean import clean_cache_files, clean_invalid_cache_entries, delete
20+
from .clean import (
21+
clean_cache_files,
22+
clean_invalid_cache_entries,
23+
delete,
24+
expire_cache_entries,
25+
)
2126
from .decode import loads
2227
from .encode import dumps
2328

@@ -40,6 +45,7 @@
4045
"database",
4146
"delete",
4247
"dumps",
48+
"expire_cache_entries",
4349
"extra_encoders",
4450
"loads",
4551
"utils",

cacholote/clean.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import pydantic
2424
import sqlalchemy as sa
2525
import sqlalchemy.orm
26+
from sqlalchemy import BinaryExpression, ColumnElement
2627

2728
from . import config, database, decode, encode, extra_encoders, utils
2829

@@ -354,3 +355,26 @@ def clean_invalid_cache_entries(
354355
decode.loads(cache_entry._result_as_string)
355356
except decode.DecodeError:
356357
_delete_cache_entry(session, cache_entry)
358+
359+
360+
def expire_cache_entries(
361+
tags: list[str] | None = None,
362+
before: datetime.datetime | None = None,
363+
after: datetime.date | None = None,
364+
) -> None:
365+
now = utils.utcnow()
366+
367+
filters: list[BinaryExpression[bool] | ColumnElement[bool]] = []
368+
if tags is not None:
369+
filters.append(database.CacheEntry.tag.in_(tags))
370+
if before is not None:
371+
filters.append(database.CacheEntry.timestamp < before)
372+
if after is not None:
373+
filters.append(database.CacheEntry.timestamp > after)
374+
375+
with config.get().instantiated_sessionmaker() as session:
376+
for cache_entry in session.scalars(
377+
sa.select(database.CacheEntry).filter(*filters)
378+
):
379+
cache_entry.expiration = now
380+
database._commit_or_rollback(session)

tests/test_60_clean.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
from cacholote import cache, clean, config, utils
1717

1818
ONE_BYTE = os.urandom(1)
19+
TODAY = datetime.datetime.now(tz=datetime.timezone.utc)
20+
TOMORROW = TODAY + datetime.timedelta(days=1)
21+
YESTERDAY = TODAY - datetime.timedelta(days=1)
1922
does_not_raise = contextlib.nullcontext
2023

2124

@@ -30,6 +33,11 @@ def open_urls(*urls: pathlib.Path) -> list[fsspec.spec.AbstractBufferedFile]:
3033
return [fsspec.open(url).open() for url in urls]
3134

3235

36+
@cache.cacheable
37+
def cached_now() -> datetime.datetime:
38+
return datetime.datetime.now()
39+
40+
3341
@pytest.mark.parametrize("method", ["LRU", "LFU"])
3442
@pytest.mark.parametrize("set_cache", ["file", "cads"], indirect=True)
3543
def test_clean_cache_files(
@@ -301,3 +309,29 @@ def test_clean_multiple_files(tmp_path: pathlib.Path) -> None:
301309

302310
clean.clean_cache_files(0)
303311
assert len(fs.ls(dirname)) == 0
312+
313+
314+
@pytest.mark.parametrize(
315+
"tags,before,after",
316+
[
317+
(["foo"], None, None),
318+
(None, TOMORROW, None),
319+
(None, None, YESTERDAY),
320+
(["foo"], TOMORROW, YESTERDAY),
321+
],
322+
)
323+
def test_expire_cache_entries(
324+
tags: None | list[str],
325+
before: None | datetime.datetime,
326+
after: None | datetime.datetime,
327+
) -> None:
328+
with config.set(tag="foo"):
329+
now = cached_now()
330+
331+
# Do not expire
332+
clean.expire_cache_entries(tags=["bar"], before=YESTERDAY, after=TOMORROW)
333+
assert now == cached_now()
334+
335+
# Expire
336+
clean.expire_cache_entries(tags=tags, before=before, after=after)
337+
assert now != cached_now()

0 commit comments

Comments
 (0)