ProvenPin Install

Idempotent job cache with diskcache FanoutCache guarded by filelock: working versions (tested 2026-10-15)

Memoize expensive jobs in a sharded diskcache FanoutCache and run each one once across threads and processes with a filelock file lock, tested fully offline.

passed6 passed · 0 failed2026-10-15 15:58 UTC
Status
passed
Last passed
2026-10-15 15:58 UTC
Tests
6 / 6
Language
Python
Runtime
Python 3.13.16
pinned versions3
diskcache@5.6.3filelock@4.2.0pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt
.venv/bin/python -m pytest -q
test environment
Python 3.13.16
linux 6.17.0-1022-azure
x64
runner: github-actions 38764699834

The code

Exactly the files that passed. Your agent can pull them with get_recipe("diskcache-filelock-jobs") (install), or see them on GitHub.

requirements.txt
diskcache==5.6.3
filelock==4.2.0
pytest==9.1.1
jobs.py
import hashlib
import json
from pathlib import Path
from typing import Any, Callable

from diskcache import FanoutCache
from filelock import FileLock

MISSING = object()


def job_key(name: str, *args: Any) -> str:
    payload = json.dumps([name, args], sort_keys=True, default=repr)
    digest = hashlib.sha256(payload.encode()).hexdigest()[:16]
    return f"{name}-{digest}"


class JobCache:
    def __init__(self, directory: str, shards: int = 4, timeout: float = 1.0, lock_timeout: float = 5.0):
        self.directory = Path(directory)
        self.directory.mkdir(parents=True, exist_ok=True)
        (self.directory / "locks").mkdir(parents=True, exist_ok=True)
        self.cache = FanoutCache(str(self.directory / "cache"), shards=shards, timeout=timeout)
        self.lock_timeout = lock_timeout
        self._locks: dict[str, FileLock] = {}

    def _lock(self, key: str) -> FileLock:
        lock = self._locks.get(key)
        if lock is None:
            lock = self._locks[key] = FileLock(str(self.directory / "locks" / f"{key}.lock"), timeout=self.lock_timeout)
        return lock

    def run_once(self, name: str, func: Callable[..., Any], *args: Any, expire: float | None = None) -> Any:
        key = job_key(name, *args)
        hit = self.cache.get(key, default=MISSING)
        if hit is not MISSING:
            return hit
        with self._lock(key):
            hit = self.cache.get(key, default=MISSING)
            if hit is not MISSING:
                return hit
            value = func(*args)
            self.cache.set(key, value, expire=expire)
            return value

    def claim(self, name: str, *args: Any) -> bool:
        return self.cache.add(job_key(name, *args), True)

    def close(self) -> None:
        self.cache.close()
tests/test_jobs.py
import threading

import diskcache
import pytest
from filelock import Timeout as FileLockTimeout

from jobs import JobCache, job_key


@pytest.fixture
def jobs(tmp_path):
    cache = JobCache(tmp_path)
    yield cache
    cache.close()


def test_run_once_computes_once_and_reuses_result(jobs):
    calls = []

    def work(x):
        calls.append(x)
        return x * 2

    assert jobs.run_once("double", work, 21) == 42
    assert jobs.run_once("double", work, 21) == 42
    assert calls == [21]


def test_cached_none_is_a_hit_not_a_miss(jobs):
    calls = []

    def returns_none():
        calls.append(1)

    assert jobs.run_once("noop", returns_none) is None
    assert jobs.run_once("noop", returns_none) is None
    assert len(calls) == 1


def test_zero_expire_means_expire_now_not_forever(jobs):
    calls = []

    def work():
        calls.append(1)
        return "v"

    assert jobs.run_once("ttl", work, expire=0) == "v"
    assert jobs.run_once("ttl", work, expire=0) == "v"
    assert len(calls) == 2


def test_concurrent_callers_compute_once(jobs):
    barrier = threading.Barrier(2)
    calls = []
    results = {}

    def slow(x):
        calls.append(x)
        return x

    def run(tag):
        barrier.wait(timeout=5)
        results[tag] = jobs.run_once("slow", slow, "k")

    threads = [threading.Thread(target=run, args=(i,)) for i in range(2)]
    for t in threads:
        t.start()
    for t in threads:
        t.join()

    assert calls == ["k"]
    assert results == {0: "k", 1: "k"}


def test_lock_contention_raises_filelock_timeout(tmp_path):
    jobs = JobCache(tmp_path, lock_timeout=0.1)
    inside = threading.Event()
    release = threading.Event()

    def slow():
        inside.set()
        release.wait(5)
        return "done"

    holder = threading.Thread(target=lambda: jobs.run_once("slow", slow))
    holder.start()
    assert inside.wait(5)

    with pytest.raises(FileLockTimeout):
        jobs.run_once("slow", lambda: "other")
    assert not issubclass(FileLockTimeout, diskcache.Timeout)

    release.set()
    holder.join()
    assert jobs.cache.get(job_key("slow")) == "done"
    jobs.close()


def test_claim_is_atomic_and_keys_are_stable(jobs):
    assert jobs.claim("build", {"a": 1, "b": 2}) is True
    assert jobs.claim("build", {"b": 2, "a": 1}) is False
    assert job_key("f", {"a": 1, "b": 2}) == job_key("f", {"b": 2, "a": 1})
    assert job_key("f", 1) != job_key("f", 2)
pytest.ini
[pytest]
pythonpath = .

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.