Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/security-fast.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,9 @@ jobs:

- name: Run pip-audit
run: |
uv run pip-audit --desc --format json --output pip-audit-report.json
# GHSA-5239-wwwm-4pmq: pygments ReDoS in AdlLexer (dev-only, no fix available)
uv run pip-audit --desc --format json --output pip-audit-report.json \
--ignore-vuln GHSA-5239-wwwm-4pmq

- name: Upload report
if: always()
Expand Down
87 changes: 87 additions & 0 deletions docs/guides/backend-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,87 @@ Large values (1MB):
- Read p99: ~13μs per operation
```

---

### MemcachedBackend

> Requires: `pip install cachekit[memcached]`

Store cache in Memcached with consistent hashing across multiple servers:

```python notest
from cachekit.backends.memcached import MemcachedBackend, MemcachedBackendConfig
from cachekit import cache

# Use default configuration (127.0.0.1:11211)
backend = MemcachedBackend()

@cache(backend=backend)
def cached_function():
return expensive_computation()
```

**Configuration via environment variables**:

```bash
# Server list (JSON array format)
export CACHEKIT_MEMCACHED_SERVERS='["mc1:11211", "mc2:11211"]'

# Timeouts
export CACHEKIT_MEMCACHED_CONNECT_TIMEOUT=2.0 # Default: 2.0 seconds
export CACHEKIT_MEMCACHED_TIMEOUT=1.0 # Default: 1.0 seconds

# Connection pool
export CACHEKIT_MEMCACHED_MAX_POOL_SIZE=10 # Default: 10 per server
export CACHEKIT_MEMCACHED_RETRY_ATTEMPTS=2 # Default: 2

# Optional key prefix
export CACHEKIT_MEMCACHED_KEY_PREFIX="myapp:" # Default: "" (none)
```

**Configuration via Python**:

```python notest
from cachekit.backends.memcached import MemcachedBackend, MemcachedBackendConfig

config = MemcachedBackendConfig(
servers=["mc1:11211", "mc2:11211", "mc3:11211"],
connect_timeout=1.0,
timeout=0.5,
max_pool_size=20,
key_prefix="myapp:",
)

backend = MemcachedBackend(config)
```

**When to use**:
- Hot in-memory caching with sub-millisecond reads
- Shared cache across multiple processes/pods (like Redis but simpler)
- High-throughput read-heavy workloads
- Applications already using Memcached infrastructure

**When NOT to use**:
- Need persistence (Memcached is volatile — data lost on restart)
- Need distributed locking (use Redis instead)
- Need TTL inspection/refresh (Memcached doesn't support it)
- Cache values exceed 1MB (Memcached default slab limit)

**Characteristics**:
- Latency: 1-5ms per operation (network-dependent)
- Throughput: Very high (multi-threaded C server)
- TTL support: Yes (max 30 days)
- Cross-process: Yes (shared across pods)
- Persistence: No (volatile memory only)
- Consistent hashing: Yes (via pymemcache HashClient)

**Limitations**:
1. **No persistence**: All data is in-memory. Server restart = data loss.
2. **No locking**: No distributed lock support (use Redis for stampede prevention).
3. **30-day TTL maximum**: TTLs exceeding 30 days are automatically clamped.
4. **1MB value limit**: Default Memcached slab size limits values to ~1MB.
5. **No TTL inspection**: Cannot query remaining TTL on a key.

## Encrypted SaaS Pattern (Zero-Knowledge)

> *cachekit.io is in closed alpha — [request access](https://cachekit.io)*
Expand Down Expand Up @@ -672,6 +753,12 @@ If no explicit backend and no module-level default, cachekit creates a RedisBack
- You're building a typical web application
- You require multi-process or distributed caching

**Use MemcachedBackend when**:
- Hot in-memory caching with very high throughput
- Simple key-value caching without persistence needs
- Existing Memcached infrastructure you want to reuse
- Read-heavy workloads where sub-5ms latency is sufficient

**Use CachekitIOBackend when** *(closed alpha — [request access](https://cachekit.io))*:
- You want managed, zero-ops distributed caching
- Multi-region caching without operating Redis
Expand Down
4 changes: 2 additions & 2 deletions llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> Production-ready caching for Python with intelligent reliability features, pluggable backends, and Rust-powered performance.

cachekit provides intelligent caching with circuit breaker, distributed locking, Prometheus metrics, and zero-knowledge encryption. Supports multiple backends including Redis, File, and CachekitIO (managed edge cache). Designed for production workloads from simple decorators to complex multi-pod deployments.
cachekit provides intelligent caching with circuit breaker, distributed locking, Prometheus metrics, and zero-knowledge encryption. Supports multiple backends including Redis, Memcached, File, and CachekitIO (managed edge cache). Designed for production workloads from simple decorators to complex multi-pod deployments.

## Getting Started

Expand All @@ -19,7 +19,7 @@ cachekit provides intelligent caching with circuit breaker, distributed locking,
## Features

- [Serializer Guide](docs/guides/serializer-guide.md): Choose the right serializer for your data type
- [Backend Guide](docs/guides/backend-guide.md): Multi-backend architecture (Redis, File, CachekitIO managed edge cache, custom)
- [Backend Guide](docs/guides/backend-guide.md): Multi-backend architecture (Redis, Memcached, File, CachekitIO managed edge cache, custom)
- [Circuit Breaker](docs/features/circuit-breaker.md): Prevent cascading failures in distributed systems
- [Distributed Locking](docs/features/distributed-locking.md): Prevent cache stampedes in multi-pod environments
- [Zero-Knowledge Encryption](docs/features/zero-knowledge-encryption.md): Client-side AES-256-GCM security for sensitive data
Expand Down
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ data = [
"pandas>=1.3.0",
"pyarrow>=21.0.0",
]
memcached = [
"pymemcache>=4.0.0",
]

[project.urls]
Homepage = "https://github.com/cachekit-io/cachekit-py"
Expand Down Expand Up @@ -205,6 +208,7 @@ dev = [
"pytest-cov>=7.0.0",
"pytest-markdown-docs>=0.6.0",
"pytest-redis>=3.0.0",
"pymemcache>=4.0.0",
# Code quality
"basedpyright>=1.32.1",
"ruff>=0.6.0",
Expand All @@ -214,6 +218,7 @@ dev = [
"httpx>=0.28.1",
"hypothesis>=6.0.0",
"pip-audit>=2.7.0",
"requests>=2.33.0; python_version >= '3.10'",
"psutil>=5.9.0",
"python-dotenv>=1.0.0",
"pyyaml>=6.0.3",
Expand Down
15 changes: 13 additions & 2 deletions src/cachekit/backends/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""Backend storage abstraction for cachekit.

This module provides protocol-based abstraction for L2 backend storage with
dependency injection pattern. Backends can be Redis, HTTP, DynamoDB, or any
key-value store.
dependency injection pattern. Backends can be Redis, HTTP, DynamoDB, Memcached,
or any key-value store.

Public API:
- BaseBackend: Core protocol (5 methods: get, set, delete, exists, health_check)
Expand All @@ -14,6 +14,7 @@
- BackendErrorType: Error classification enum
- CapabilityNotAvailableError: Exception for missing optional capabilities
- RedisBackend: Redis implementation (default)
- MemcachedBackend: Memcached implementation (requires pymemcache)

Usage:
>>> from cachekit.backends import BaseBackend, RedisBackend, BackendError
Expand Down Expand Up @@ -56,6 +57,7 @@
"BackendErrorType",
"CapabilityNotAvailableError",
"RedisBackend",
"MemcachedBackend",
]


Expand Down Expand Up @@ -95,3 +97,12 @@ def get_backend(self) -> BaseBackend:
>>> backend.set("key", b"value", ttl=60) # doctest: +SKIP
"""
...


def __getattr__(name: str):
"""Lazy import for optional backends (pymemcache may not be installed)."""
if name == "MemcachedBackend":
from cachekit.backends.memcached import MemcachedBackend

return MemcachedBackend
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
25 changes: 25 additions & 0 deletions src/cachekit/backends/memcached/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Memcached backend for cachekit.

Provides Memcached storage backend using pymemcache with consistent hashing
for multi-server support. Thread-safe via HashClient connection pooling.

Public API:
- MemcachedBackend: Main backend implementation
- MemcachedBackendConfig: Configuration class

Example:
>>> from cachekit.backends.memcached import MemcachedBackend, MemcachedBackendConfig
>>> config = MemcachedBackendConfig(servers=["127.0.0.1:11211"])
>>> backend = MemcachedBackend(config) # doctest: +SKIP
>>> backend.set("key", b"value", ttl=60) # doctest: +SKIP
"""

from __future__ import annotations

from cachekit.backends.memcached.backend import MemcachedBackend
from cachekit.backends.memcached.config import MemcachedBackendConfig

__all__ = [
"MemcachedBackend",
"MemcachedBackendConfig",
]
Loading
Loading