A lightweight Python library for Redis-backed tenant configuration storage
Project description
litewave-cache-lib
A lightweight Python library providing two complementary packages for Redis-backed tenant configuration storage:
| Package | Purpose |
|---|---|
tenant_mgr_cache |
Read-only cache reader — look up tenant secrets already stored in Redis |
cache_manager |
Full cache manager — read from Redis and optionally auto-fetch from the tenant-manager API on a cache miss |
Installation
pip install git+https://github.com/aiorch/litewave-cache-lib
Or install from source:
git clone https://github.com/aiorch/litewave-cache-lib.git
cd litewave-cache-lib
pip install .
Requirements: Python >= 3.10, redis >= 5.0.0, httpx >= 0.25.0, python-dotenv >= 1.0.0
tenant_mgr_cache
A lightweight, read-only cache reader for tenant secrets stored in Redis. Provides three simple functions covering all lookup patterns.
Features
- Connection-pooled Redis client with automatic retry on failure (5-minute cooldown)
- Fetch values by raw Redis key, or by
(tenant_id, secret_name)pair viaget_secret - Attribute-level extraction from stored JSON objects via
get_attribute_value_by_key - Automatic JSON deserialization of stored values
- Standardised tenant key format:
tenant:{tenant_id}:secret:{secret_name} - Zero-boilerplate logging setup
Configuration
Settings are read from environment variables at import time (CacheSettings is instantiated on import). Always call load_dotenv() before importing from tenant_mgr_cache.
| Environment Variable | Required | Default | Description |
|---|---|---|---|
REDIS_HOST |
yes | — | Redis server hostname |
REDIS_PORT |
yes | — | Redis server port |
REDIS_PASSWORD |
yes | — | Redis password |
REDIS_TENANT_CONFIG_DB |
no | 0 |
Redis database index for tenant config |
REDIS_MAX_CONNECTIONS |
no | 50 |
Connection pool size |
REDIS_SOCKET_CONNECT_TIMEOUT |
no | 2 |
Socket connect timeout (seconds) |
REDIS_SOCKET_TIMEOUT |
no | 2 |
Socket read/write timeout (seconds) |
LOG_LEVEL |
no | INFO |
Logging level (DEBUG, INFO, WARNING, …) |
Usage
Important:
load_dotenv()must be called before importingtenant_mgr_cachebecause environment variables are resolved at module load time.
from dotenv import load_dotenv
load_dotenv(".env", override=True)
from tenant_mgr_cache import (
get_attribute_value_by_key,
get_secret,
get_value_by_key,
)
Case 1 — fetch by fully-formed Redis key
Pass a complete, pre-built Redis key directly:
key = "tenant:15:secret:satoken"
value = get_value_by_key(key)
if value is not None:
print(f"{key} → {value}")
else:
print("Key not found or Redis unavailable.")
Case 2 — fetch by tenant ID and secret name
The canonical key tenant:{tenant_id}:secret:{secret_name} is built internally:
value = get_secret("15", "satoken")
if value is not None:
print(f"secret → {value}")
else:
print("Not found.")
JSON objects stored in Redis are deserialized automatically; plain strings are returned as-is.
Case 3 — fetch a single attribute from a stored JSON object
Builds the key, fetches the JSON object, and returns the value of the requested attribute:
value = get_attribute_value_by_key("15", "satoken", "api_key")
if value is not None:
print(f"api_key → {value}")
else:
print("Attribute not found.")
API Reference
get_value_by_key(key: str) -> Optional[Any]
Fetches the value stored at key and JSON-decodes it where possible. Returns None when the key does not exist, Redis is unavailable, or an error occurs.
get_secret(tenant_id: str, secret_name: str) -> Optional[Any]
Builds the canonical key tenant:{tenant_id}:secret:{secret_name} and returns the stored value. JSON is deserialized automatically. Returns None if the key does not exist or Redis is unavailable.
get_attribute_value_by_key(tenant_id: str, secret_name: str, attribute_name: str) -> Optional[Any]
Builds the canonical key, fetches the stored JSON object, and returns data.get(attribute_name). Falls back to getattr for non-dict objects. Returns None if the key is missing, the value is not dict-like, or the attribute is absent.
get_redis_client() -> Optional[redis.Redis]
Returns the shared, connection-pooled Redis client. Returns None if the connection is unavailable. A failed connection is retried automatically after a 5-minute cooldown; subsequent calls reuse the existing client.
prepare_key(tenant_id: str, secret_name: str) -> str
Builds the canonical Redis key:
from tenant_mgr_cache.cache_client import prepare_key
key = prepare_key("15", "satoken")
print(key) # "tenant:15:secret:satoken"
settings — CacheSettings
A dataclass instance holding all resolved configuration values:
from tenant_mgr_cache import settings
print(settings.REDIS_HOST)
print(settings.REDIS_PORT)
print(settings.REDIS_TENANT_CONFIG_DB)
logger — logging.Logger
A pre-configured logger (litewave_cache) that respects LOG_LEVEL:
from tenant_mgr_cache import logger
logger.info("Custom message from application code")
cache_manager
A higher-level cache manager that wraps a RedisClient and an optional TenantService. When self_managed_cache is enabled, a cache miss automatically fetches the secret from the tenant-manager HTTP API and populates Redis (default TTL: 48 hours).
Features
- Reads from Redis first; falls back to tenant-manager API on a cache miss (when enabled)
- Auto-populates Redis after a successful tenant-manager fetch (48-hour TTL by default)
- Fully configurable via constructor arguments or environment variables
- Low-level
RedisClientandTenantServiceclasses are also exported for direct use
Configuration
Redis connection (RedisClient)
| Environment Variable | Required | Default | Description |
|---|---|---|---|
REDIS_HOST |
yes | — | Redis server hostname |
REDIS_PORT |
yes | — | Redis server port |
REDIS_PASSWORD |
no | None |
Redis password |
REDIS_TENANT_CONFIG_DB |
no | 0 |
Redis database index |
REDIS_MAX_CONNECTIONS |
no | 50 |
Connection pool size |
REDIS_SOCKET_CONNECT_TIMEOUT |
no | 2.0 |
Socket connect timeout (seconds) |
REDIS_SOCKET_TIMEOUT |
no | 2.0 |
Socket read/write timeout (seconds) |
Cache manager behaviour
| Environment Variable | Required | Default | Description |
|---|---|---|---|
SELF_MANAGED_CACHE |
no | false |
Set to true to enable auto-fetch from tenant-manager |
Tenant-manager API (TenantService)
Only required when self_managed_cache is True:
| Environment Variable | Required | Description |
|---|---|---|
TENANT_MANAGER_BASE_URL |
yes | Base URL of the tenant-manager API |
SEED_TENANT_TOKEN |
yes | Bearer token used to authenticate requests |
SEED_TENANT_ID |
yes | Tenant ID sent as the x-tenant-id header |
Usage
from dotenv import load_dotenv
from cache_manager import CacheManager
load_dotenv()
manager = CacheManager(self_managed_cache=True, ttl=10 * 60)
data = manager.get_secret_data("15", "azureConfiguration")
if data is None:
print("Secret not found or tenant-manager unavailable")
else:
print(f"Secret: {data}")
Resolution order inside get_secret_data
- Redis cache — returns immediately on a hit.
- Tenant-manager API — called only when
self_managed_cache=True; the response is stored in Redis with the configured TTL before being returned. - Returns
Nonewhen both sources are unavailable or the secret does not exist.
API Reference
CacheManager(redis_client?, tenant_service?, self_managed_cache?, ttl?)
| Parameter | Type | Default | Description |
|---|---|---|---|
redis_client |
RedisClient |
auto-constructed from env | Pre-built Redis client |
tenant_service |
TenantService |
auto-constructed when self_managed_cache=True |
Pre-built tenant service |
self_managed_cache |
bool |
SELF_MANAGED_CACHE env var |
Enable auto-fetch on cache miss |
ttl |
int (seconds) |
172800 (48 h) |
TTL applied when writing to Redis |
CacheManager.get_secret_data(tenant_id: str, secret_name: str) -> Optional[Any]
Retrieves the secret for the given tenant_id / secret_name pair following the resolution order described above.
RedisClient — low-level Redis wrapper
Can be used independently when direct Redis access is needed:
from cache_manager import RedisClient
client = RedisClient() # all settings from env vars
client = RedisClient(host="localhost", port=6379, db=1) # override specific params
client.set("my:key", {"foo": "bar"}, ttl=3600) # JSON-serialized, 1-hour TTL
data = client.get("my:key") # {"foo": "bar"}
client.set("config:flag", "enabled")
flag = client.get("config:flag") # "enabled"
missing = client.get("does:not:exist") # None
TenantService — HTTP client for the tenant-manager API
Can be used independently when direct API access is needed:
from cache_manager import TenantService
service = TenantService(
base_url="https://tenant-manager.internal",
seed_token="my-token",
seed_tenant_id="1",
)
secret = service.get_secret("15", "azureConfiguration")
Development
Setup
pip install -r requirements.txt
Running Tests
pytest
With coverage:
pytest --cov=tenant_mgr_cache --cov-report=term-missing
License
MIT License. See LICENSE for details.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file litewave_cache_lib-0.1.4.tar.gz.
File metadata
- Download URL: litewave_cache_lib-0.1.4.tar.gz
- Upload date:
- Size: 223.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.25
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7731b7d0a41c48aafa42642be1857a78443589d195cd31afc1596cac4a0e3c45
|
|
| MD5 |
31191a9f36751ab8c4f5a581ef8c0606
|
|
| BLAKE2b-256 |
dc1e8c1a787e2d51dd2a118d01a78efe6e7487f84d25bb946213c4e969edf8be
|
File details
Details for the file litewave_cache_lib-0.1.4-py3-none-any.whl.
File metadata
- Download URL: litewave_cache_lib-0.1.4-py3-none-any.whl
- Upload date:
- Size: 21.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.25
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eb398cea516f4db8195693452511902bd070f22c97beecfe8d0a53507abe07fa
|
|
| MD5 |
379df58063f62443d699bc3ad724585d
|
|
| BLAKE2b-256 |
c2159089f05e726e5b59ddb481c9fdd9fa022345016fe4010744d991ac991c52
|