storage-verse-avneesh
Async storage router across Redis, Upstash, PostgreSQL, and future backends.
Backends are grouped by category, not forced into one shape: redis and
upstash live under backends/cache/
sharing a get/set interface; postgres lives under
backends/database/ with its
own connection-pool-and-query API, since SQL access doesn't fit a key-value
shape. Each backend has its own folder and its own self-contained code.
Unlike an LLM call (one shape: prompt in, text out), storage operations are
heterogeneous — get, set, incr, delete all return different native
types. So instead of one Router.get_response(...) wrapping every call in a
uniform envelope, this library gives you a small, cached client per
backend that returns plain Python types and raises typed exceptions on
failure.
Install
pip install "storage-verse-avneesh[redis]" # Redis only
pip install "storage-verse-avneesh[upstash]" # Upstash only
pip install "storage-verse-avneesh[database]" # PostgreSQL (asyncpg) only
pip install "storage-verse-avneesh[all]" # all three
Usage
import asyncio
from storage_verse_avneesh import get_store
async def main():
store = get_store("redis", url="redis://localhost:6379/0")
await store.set("greeting", "hello", ttl_seconds=60)
print(await store.get("greeting")) # "hello"
print(await store.incr("visits")) # 1
await store.delete("greeting")
asyncio.run(main())
For Upstash: get_store("upstash", url="...", token="...") (both from the
Upstash console).
PostgreSQL (a database, not a cache, backend)
postgres doesn't implement get/set — it's a connection-pool manager
with a query API, since SQL access needs queries and transactions, not
key-value operations. Configuration comes from environment variables
(ENVIRONMENT, DATABASE_URL or DB_HOST/DB_PORT/DB_USER/
DB_PASSWORD/DB_NAME), read lazily the first time a pool is actually
created — not from get_store() kwargs:
import asyncio
from storage_verse_avneesh import get_store
async def main():
db = get_store("postgres") # no config kwargs
row = await db.fetchrow("SELECT * FROM users WHERE id = $1", 1)
count = await db.fetchval("SELECT count(*) FROM users")
await db.execute("UPDATE users SET last_seen = now() WHERE id = $1", 1)
async with db.transaction() as conn:
await conn.execute("UPDATE accounts SET balance = balance - $1 WHERE id = $2", 100, 1)
await conn.execute("UPDATE accounts SET balance = balance + $1 WHERE id = $2", 100, 2)
# commits on clean exit, rolls back automatically if either line raises
asyncio.run(main())
execute/fetch/fetchrow/fetchval each pull a connection from the
managed pool automatically and raise StorageOperationError on a Postgres
failure (matching how the cache backends report failures). Need something
these don't cover (LISTEN/NOTIFY, prepared statements, cursors)? Drop to
pool = await db.get_pool() (or await db.wait_for_connection_pool() for
retry-with-backoff) for direct asyncpg access.
The manager handles pool health checks (dead pools are transparently
recreated), Postgres date/time codec registration, and clean shutdown
via await db.close().
Connection reuse
Call get_store(name, **config) from anywhere in your codebase — you don't
need to construct a client once and pass it around manually. The first call
for a given (name, config) pair constructs the backend (and its
connection pool); every later call with the same name and config, from
any module, returns that exact same cached instance instead of reconnecting:
# file_a.py
store = get_store("redis", url=REDIS_URL)
# file_b.py — same instance as file_a.py, no new connection made
store = get_store("redis", url=REDIS_URL)
Each backend's underlying client also pools connections internally
(redis-py's async client, Upstash's HTTP client), so concurrent calls
through the same cached instance don't open a new connection per operation
either — reuse happens at both the instance level (this library's cache)
and the transport level (the backend's own client).
Call await close_all() once, on app shutdown, to close every cached
backend and clear the cache.
Discovering backends
import storage_verse_avneesh as sv
sv.help() # documents list_backends(), backend_info(), get_store()
sv.list_backends() # [{"name": "redis", "display_name": "Redis", "category": "cache"}, ...]
sv.backend_info("upstash") # what it needs to construct, and what it supports
backend_info(name) tells you what a backend needs (e.g. redis needs
url; upstash needs url and token) and what it supports — e.g.
upstash has no pub/sub or multi-command transactions, since its REST
protocol has no persistent connection for either. Raises
BackendNotFoundError for an unknown name.
Registered backends
name |
Category | Notes |
|---|---|---|
redis |
cache | pub/sub and transactions supported |
upstash |
cache | no pub/sub, no multi-command transactions (REST-only) |
postgres |
database | execute/fetch/fetchrow/fetchval/transaction, not get/set - see above |
The CacheBackend protocol
Every key-value backend implements the same structural interface
(protocols.py):
async def get(self, key: str) -> Optional[str]: ...
async def set(self, key: str, value: str, ttl_seconds: Optional[int] = None) -> bool: ...
async def delete(self, key: str) -> int: ...
async def exists(self, key: str) -> bool: ...
async def expire(self, key: str, ttl_seconds: int) -> bool: ...
async def incr(self, key: str, amount: int = 1) -> int: ...
async def ping(self) -> bool: ...
async def close(self) -> None: ...
This is why redis and upstash are genuinely interchangeable for
key-value use — code written against CacheBackend works with either.
postgres deliberately does not implement this protocol — it's a
database-category backend with its own shape (see above). Future
non-key-value backends (document stores, etc.) will get their own protocol
too, rather than being forced into CacheBackend.
Exceptions
All exceptions inherit from StorageError:
BackendNotFoundError—nameisn't registered.StorageConnectionError— a backend couldn't be reached (connect/ping failure).StorageOperationError— a specific operation (get/set/...) failed.
Development
pip install -e ".[dev]"
pytest
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 storage_verse_avneesh-0.1.0.tar.gz.
File metadata
- Download URL: storage_verse_avneesh-0.1.0.tar.gz
- Upload date:
- Size: 21.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ceebfebd7efd303ddc42ffde228f5b178570e660b4ef2608ca084a2d927beda6
|
|
| MD5 |
47d722849627bf7f1fd624184bec2cef
|
|
| BLAKE2b-256 |
1d0e85a3a4119c59dcb7cef92f13092fcc7bf424cf47d6337c4e93c288e56b19
|
Provenance
The following attestation bundles were made for storage_verse_avneesh-0.1.0.tar.gz:
Publisher:
publish.yml on avneeshrai07/storage-verse-avneesh
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
storage_verse_avneesh-0.1.0.tar.gz -
Subject digest:
ceebfebd7efd303ddc42ffde228f5b178570e660b4ef2608ca084a2d927beda6 - Sigstore transparency entry: 2320013367
- Sigstore integration time:
-
Permalink:
avneeshrai07/storage-verse-avneesh@3ef7d395d9a77e24d141626266d7137aec03f9fc -
Branch / Tag:
refs/heads/main - Owner: https://github.com/avneeshrai07
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3ef7d395d9a77e24d141626266d7137aec03f9fc -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file storage_verse_avneesh-0.1.0-py3-none-any.whl.
File metadata
- Download URL: storage_verse_avneesh-0.1.0-py3-none-any.whl
- Upload date:
- Size: 20.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e4a7494e98a1debc63001e523ef8da61bd0c07943b6a01f82299a99b1ff23494
|
|
| MD5 |
7de1751318f69d72417336a04a34ee21
|
|
| BLAKE2b-256 |
cfa6620cc0241f637a597f0036bd252014378ea1f5958969b21e2954f80fdbd1
|
Provenance
The following attestation bundles were made for storage_verse_avneesh-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on avneeshrai07/storage-verse-avneesh
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
storage_verse_avneesh-0.1.0-py3-none-any.whl -
Subject digest:
e4a7494e98a1debc63001e523ef8da61bd0c07943b6a01f82299a99b1ff23494 - Sigstore transparency entry: 2320013693
- Sigstore integration time:
-
Permalink:
avneeshrai07/storage-verse-avneesh@3ef7d395d9a77e24d141626266d7137aec03f9fc -
Branch / Tag:
refs/heads/main - Owner: https://github.com/avneeshrai07
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3ef7d395d9a77e24d141626266d7137aec03f9fc -
Trigger Event:
workflow_dispatch
-
Statement type: