aci-cache
Drop-in Redis cache wrapper that automatically adapts its invalidation strategy to your write traffic.
Most caching libraries make you choose a strategy upfront and stick with it. aci-cache monitors your write rate in real time and switches between strategies automatically — so you get low overhead at low traffic and strong consistency at high traffic, with no manual tuning.
How it works
The library tracks writes per second in a sliding window. Based on configurable thresholds, it picks one of three strategies:
| Write Rate | Strategy | Behaviour |
|---|---|---|
| < 10 writes/sec | TTL | Rely on Redis key expiry. Zero overhead. |
| 10–50 writes/sec | Batched | Buffer keys, flush a single Pub/Sub message every 2.5s. |
| > 50 writes/sec | Eager | Publish an invalidation message on every write. |
In a multi-instance deployment, one instance runs as the controller (monitors rate, decides strategy) and broadcasts decisions to all others via a Redis Pub/Sub channel. All instances subscribe and apply the strategy change locally — keeping caches consistent across your entire fleet.
Installation
Python
pip install aci-cache
Node.js
npm install aci-cache
Quick start
Python
import redis
from aci_cache import AdaptiveCache
r = redis.Redis(host="localhost", port=6379)
cache = AdaptiveCache(r)
cache.set("user:42", '{"name": "Alice"}')
data = cache.get("user:42") # '{"name": "Alice"}'
print(cache.strategy) # "ttl" | "batched" | "eager"
print(cache.stats.total_writes)
Async (asyncio)
import redis.asyncio as aioredis
from aci_cache import AsyncAdaptiveCache
async def main():
r = aioredis.Redis()
async with AsyncAdaptiveCache(r) as cache:
await cache.set("session:99", "active")
print(await cache.get("session:99"))
Node.js (TypeScript)
import Redis from "ioredis";
import { AdaptiveCache } from "aci-cache";
const cache = new AdaptiveCache({ redis: new Redis() });
await cache.start();
await cache.set("user:42", JSON.stringify({ name: "Alice" }));
const data = await cache.get("user:42");
console.log(cache.strategy); // "ttl" | "batched" | "eager"
console.log(cache.statsSnapshot.totalWrites);
await cache.stop();
Multi-instance setup
Run one instance as the controller. All others follow.
Python
# Instance 1 — controller (monitors write rate, publishes decisions)
controller_cache = AdaptiveCache(redis.Redis(), is_controller=True)
# Instance 2, 3, … — followers
follower_cache = AdaptiveCache(redis.Redis(), is_controller=False)
Node.js
// Instance 1
const cache = new AdaptiveCache({ redis, isController: true });
// Instances 2+
const cache = new AdaptiveCache({ redis, isController: false });
Configuration
All parameters have defaults. Override what you need.
| Parameter | Default | Description |
|---|---|---|
high_threshold / highThreshold |
50 |
Writes/sec that triggers eager mode |
low_threshold / lowThreshold |
10 |
Writes/sec that drops back to TTL mode |
default_ttl / defaultTtl |
10 |
Key TTL in seconds |
batch_interval / batchInterval |
2.5 |
Seconds between batch flushes |
controller_interval / controllerInterval |
3 |
Seconds between controller rate checks |
sliding_window / slidingWindow |
5 |
Write rate measurement window in seconds |
namespace |
None / undefined |
Key/channel prefix for multi-service Redis |
is_controller / isController |
True / true |
Whether this instance runs the controller |
Python example
cache = AdaptiveCache(
redis.Redis(),
high_threshold=100,
low_threshold=20,
default_ttl=30,
namespace="user-service",
is_controller=True,
)
Node.js example
const cache = new AdaptiveCache({
redis,
highThreshold: 100,
lowThreshold: 20,
defaultTtl: 30,
namespace: "user-service",
isController: true,
});
Observability
stats = cache.stats
print(stats.total_reads) # int
print(stats.cache_hits) # int
print(stats.cache_misses) # int
print(stats.total_writes) # int
print(stats.current_strategy) # "ttl" | "batched" | "eager"
print(stats.write_rate) # float (writes/sec)
# React to strategy switches
cache.on_switch(lambda from_s, to_s: print(f"{from_s} → {to_s}"))
Repository layout
aci-cache/
├── src/aci_cache/ # Python package source
│ ├── cache.py # AdaptiveCache (sync)
│ ├── async_cache.py # AsyncAdaptiveCache (asyncio)
│ ├── config.py # CacheConfig
│ ├── controller.py # Background rate monitor
│ ├── subscriber.py # Pub/Sub listener
│ ├── tracker.py # Sliding-window write rate tracker
│ ├── stats.py # Stats collector
│ └── strategies/ # TTL, Eager, Batched strategy classes
├── tests/ # Python test suite (99 tests)
├── node/ # Node.js/TypeScript package
│ ├── src/ # TypeScript source (mirrors Python)
│ └── tests/ # TypeScript test suite (63 tests)
├── pyproject.toml # Python package config
└── .github/workflows/ # CI (test) + CD (publish on tag)
Development
Python
python -m venv venv && source venv/bin/activate
pip install -e ".[dev]" pytest-asyncio
pytest tests/ -v
Node.js
cd node
npm install
npm test
npm run build
Releasing a new version
- Bump
versioninpyproject.tomlandnode/package.json - Commit and tag:
git commit -am "feat: v0.2.0"
git tag v0.2.0
git push origin main --tags
GitHub Actions will run all tests, publish to PyPI and npm, and create a GitHub Release automatically.
License
MIT — see LICENSE.
Release files for aci-cache 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| aci_cache-0.1.1.tar.gz | 21.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aci_cache-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 43.7 kB
Release files / aci_cache-0.1.1.tar.gz
| Download URL | aci_cache-0.1.1.tar.gz |
|---|---|
| Size | 21.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e9d84ed8009da1f36f883a59294f7ce4619809e5de3e0dc16b20c5da82f3fa5b
|
|
BLAKE2b-256 checksum How to use checksums |
1c8ef71259739c792e75d0b0f4a74f7750357d1c683ee3be5203d0e01a8a8188
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Release files / aci_cache-0.1.1-py3-none-any.whl
| Download URL | aci_cache-0.1.1-py3-none-any.whl |
|---|---|
| Size | 22.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
963ce1d3c6facec51b4a868f5ba8ad425dc21180227291494d24fc090b9d87d0
|
|
BLAKE2b-256 checksum How to use checksums |
9df4051051a12e157566f0b46c1f572f1f5b4fea5d7d811301fe3ba98b3d8970
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|