Releases: jolovicdev/cashet
Releases · jolovicdev/cashet
Release 0.4.4: async tasks, nested refs, and hash correctness (#22)
- Fix:
AsyncClientnow awaitsasync deftask functions correctly. - Fix: function hashes include immutable globals used by nested code objects, plus stable built-ins like
range,slice, anddatetimevalues. - Fix: nested
ResultRef/AsyncResultRefvalues resolve inside containers and input refs are deduplicated in commit metadata. - Fix: nested ref resolution preserves tuple subclasses and avoids invalid dict/frozenset containers from unhashable resolved values.
- Fix: task functions returning awaitable objects keep those objects as the actual cached result.
- Fix: base installs raise a clear
cashet[redis]extra error when Redis backends are imported without Redis dependencies.
Release 0.4.3: hotfix freezegun dep, Redis race guard, flaky heartbeat test (#8)
- Fix: add
freezegunto dev dependencies — TTL/GC tests no longer fail withModuleNotFoundErroron fresh installs or CI. - Fix: restore
expires_at <= nowrace guard in Redisfind_by_fingerprint— a commit can expire between theZREVRANGEBYSCOREserver-side filter and theget_commitcall. - Fix: bump heartbeat test
running_ttlfrom 100ms to 3s — 100ms is too tight for CI runners, causing sporadic stale claim reclamation.
Release 0.4.2: tag-index invalidation perf, Redis TTL pushdown, invalidate CLI (#7)
- Perf:
delete_by_tagsin SQLite batches all matching rows into a single DELETE with one orphan-detection pass — previously O(n) row-by-row calls with per-row orphan queries. - Perf:
delete_by_tagsin Redis now uses tag-set indexes (cashet:tag:{key},cashet:tag:{key}:{value}) with SINTER — previously a fullzrevrange(all)scan fetching every commit. - Perf:
find_by_fingerprintin Redis pushes TTL filtering server-side viaZREVRANGEBYSCOREusingexpires_attimestamp as the sorted-set score — previously Python-iterated all candidates checkingexpires_atclient-side. - Feat:
cashet invalidate -t key=value/-t keyCLI command. - Test: replaced
time.sleepwithfreezegunin 3 TTL/GC tests for deterministic execution. - Test: added Redis
delete_by_tagscoverage (exact match, bare key, multi-condition) and CLIinvalidatetests.
Release 0.4.1: hash prefix fix, TTL perf, and test coverage (#6)
- Fix: hash prefix lookups normalized to lowercase — uppercase hex chars (A-F) no longer fail to match SHA-256 digests.
- Fix:
idx_last_accessed_atcreation moved after the column migration — opening a pre-migration database no longer crashes with "no such column". - Perf: TTL expiration filter pushed into SQL WHERE clause instead of O(n) Python iteration.
- Test: ambiguous prefix
delete_commitrollback verified to not poison subsequent writes. - Test: TTL coverage for
submit_manylist and dict paths, including re-execution after expiry. - Docs: CHANGELOG.md added, covering all versions back to 0.1.1.
Release 0.4.0
Fixed
- Hash prefix validation — prevents
*,?,_from reaching SQLLIKEand Redis glob patterns - Stale claim sync — reclaims now copy current
cache,force,timeout,tagsinstead of stale config - SQLite schema migrations — forward-migrates
forceandtimeout_secondscolumns safely - Delete rollback — early returns in
delete_commit()no longer leave dangling transactions - Import integrity —
import_archive()now SHA-256-verifies blobs before writing
New
- Per-entry TTL —
submit(_ttl=...),@client.task(ttl=...), auto-expiry viaCommit.expires_at - Tag-based invalidation —
client.invalidate(tags={...})deletes matching commits atomically - TTL-aware lookups —
find_by_fingerprint()andget_history()skip expired commits
Tests: 325 passed. Ruff clean. Pyright clean.
Release v0.3.2
0.3.1
Core Architecture
- Extract shared client base (
_client_base.py), async runner (_runner.py), and sync store adapter (adapters.py) - Unify batch execution and executor paths between sync and async clients
Store Backends
- SQLite: Deduped file locks per path with
_SQLiteLockStateto prevent lock churn - Redis: O(1) blob stats via maintained counters instead of O(n) scans
- Redis: Atomic Lua script for orphan cleanup with stats decrement
- Redis: Eviction backfills partial
last_accessedindex before removing old commits - Redis:
put_commitref-count race fixed with WATCH/MULTI/EXEC optimistic locking
Server
- Task registration with JSON args
- Bearer token authentication (
require_token) - Remote code gating (
allow_remote_coderequires non-empty token) - Configurable request size limits (default: 500MB)
- Generic 500 error responses, no traceback leakage
Hashing
- Deterministic cache keys for defaults, module names, bytecode, and custom task names
AsyncExecutor Protocol
- Pluggable async executors via
AsyncExecutorprotocol - Enables distributed executor implementations (Celery, Kafka, RQ)
Thread Pool Fix
BlockingAsyncRunnersets 64-worker default pool instead ofmin(32, cpu_count+4)to prevent lock contention deadlock on low-core machines
Migration
Redis blob data keys renamed from cashet:blob:{hash} to cashet:blob:data:{hash}. Clear old caches before upgrading if using Redis.
v0.3.0: async client, Redis backend, HTTP server
What's new
- Async client —
AsyncClientwithsubmit(),submit_many(),serve()mirroring the sync API, plusAsyncResultRefwith concurrent-safe asyncload() - Redis backend —
RedisStoreandAsyncRedisStorewith cross-process per-fingerprint locking and O(1) blob ref-counting for efficient GC - HTTP server — Starlette + uvicorn with sync and async apps (
client.serve()), REST endpoints for submit, result, commit, log, stats, and gc - Async SQLite store —
AsyncSQLiteStorewith inline blob auto_vacuum and VACUUM on close/evict - Async executor —
AsyncLocalExecutorwith asyncio-native heartbeat and locking,asyncio.to_thread()for CPU-bound work - Extensible extras —
cashet[redis]andcashet[server]pip extras for optional backends - Batch refactor —
submit_manyextracted to_batch.py, shared by both sync and async clients
Full diff: v0.2.0...v0.3.0
v0.2.0 — force rerun, task timeouts, parallel batch, inline storage, size-based GC
What's new
force=True— bypass cache and always re-execute, on bothsubmit()and@client.task- Task timeouts — per-task timeout with optional retry (
_timeout=30,@client.task(timeout=30)) - Parallel batch execution —
submit_many(..., max_workers=N)for DAG-aware fan-out - Inline blob storage — small objects (<1KB) stored in SQLite for reduced filesystem overhead
- Size-based GC —
gc --max-size 1GBevicts oldest entries until under the limit TaskError— now exported from the package
Full diff: 04866df...v0.2.0