omnixys-cache
Omnixys shared cache package — a typed Redis/Valkey facade with JSON serialization, invalidation, distributed locks, rate limiting, streams with consumer groups, and delayed jobs, all on one client.
Installation
pip install omnixys-cache
Features
- CacheClient: async Redis/Valkey facade with typed keys, JSON serialization, TTL handling, atomic
NX/PXops and low-levelrawaccess - Key definitions:
create_key(prefix, ttl_seconds)andkey(...)builders validated at construction time - CacheLock: distributed mutual-exclusion lock with token-based (WATCH/MULTI) release
- RateLimiter: fixed-window rate limiting with
hit/checkand TTL window reset - ValkeyStreamService: stream + consumer-group queues (
ensureGroup,enqueue,consume,read_pending,ack) - Delayed jobs: schedule/claim/retry/cancel delayed jobs with a worker loop and decorator-driven registry
- Invalidation:
CacheInvalidationServicewith pluggable strategies - Serialization: JSON serializers (
JsonCacheSerializer,StrictJsonCacheSerializer) - DI:
CacheProvidercontainer integration (dishka)
Usage
Client and keys
import asyncio
from cache import CacheClient, create_key
session = create_key("session", ttl_seconds=300)
cache = CacheClient(url="redis://localhost:6379", key_prefix="myapp")
async def main() -> None:
await cache.set_json(session, {"userId": 42}, parts=("u1",)) # myapp:session:u1
assert await cache.get_json(session, "u1") == {"userId": 42}
await cache.expire(session, 60, "u1")
await cache.close()
asyncio.run(main())
Raw keys work too — pass a plain str and optional parts are joined with :.
Distributed lock
from cache import CacheClient, CacheLock
async with CacheLock(cache, "deploy:eu1", ttl_ms=30_000, wait_ms=10_000) as lock:
...
# or manually:
lock = CacheLock(cache, "deploy:eu1", ttl_ms=30_000)
if await lock.acquire():
try:
...
finally:
await lock.release()
Rate limiting
from cache import CacheClient, RateLimiter
limiter = RateLimiter(cache)
if not await limiter.check("sms:user:42", limit=5, ttl_seconds=60).allowed:
raise RuntimeError("rate limit exceeded")
Streams with consumer groups
from cache import CacheClient, ValkeyStreamService
streams = ValkeyStreamService(cache)
await streams.ensure_group("jobs", "workers")
await streams.enqueue("jobs", {"type": "resize", "imageId": "abc"})
for message in await streams.consume("jobs", "workers", consumer="w1", block_ms=1_000):
payload = message.data # {"type": "resize", "imageId": "abc"}
await streams.ack("jobs", "workers", message.id)
Delayed jobs
from cache import DelayedJobService, DelayedJobSchedule, DelayedJobWorker, delayed_job
@delayed_job("image.resize")
async def resize(payload: dict) -> None:
print(payload["imageId"])
service = DelayedJobService(cache)
job_id = await service.schedule(
DelayedJobSchedule(type="image.resize", payload={"imageId": "abc"}, delay_ms=5_000)
)
worker = DelayedJobWorker(service) # uses the default (decorated) registry
await worker.start()
Jobs are persisted as records (7-day TTL), scheduled on a sorted set (delayed:jobs), claimed atomically via WATCH/MULTI, retried on failure up to max_retries, and surfaced through status(), cancel(), retry(), and claim_due().
Testing
The package ships behavior-level tests (pytest + fakeredis, no server needed):
uv run pytest -q # 47 tests
uv run ruff check . # lint
uv run mypy src/ # strict typing
License
GPL-3.0-or-later
Metadata
Release files for omnixys-cache 5.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| omnixys_cache-5.0.0.tar.gz | 46.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| omnixys_cache-5.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 60.4 kB
Release files / omnixys_cache-5.0.0.tar.gz
| Download URL | omnixys_cache-5.0.0.tar.gz |
|---|---|
| Size | 46.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
07af742a448bbd4a40bf6eb135eb3bb79c23da19ee5e9b4f69258ed8f738a577
|
|
BLAKE2b-256 checksum How to use checksums |
f3d8c3facaa37900b618565bf160f5c23bc6a0762a99b7563d05093d660ea80d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.8 {"installer":{"name":"uv","version":"0.12.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / omnixys_cache-5.0.0-py3-none-any.whl
| Download URL | omnixys_cache-5.0.0-py3-none-any.whl |
|---|---|
| Size | 14.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7a51503018c401b548ad7a59712542698349a9ae8a5e403e3459de58b7123331
|
|
BLAKE2b-256 checksum How to use checksums |
5e3d6c777eb0d625510019e80309fbeab17e090aacc67545b0bf5c6daff735f2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.8 {"installer":{"name":"uv","version":"0.12.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|