init: async file-backed kv store for single-process local state
Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
commit
019955dad3
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# claude
|
||||||
|
CLAUDE.md
|
||||||
|
|
||||||
|
# python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.eggs/
|
||||||
|
|
||||||
|
# env
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
.env
|
||||||
|
.pytest_cache/
|
||||||
86
README.md
Normal file
86
README.md
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# aiokv
|
||||||
|
|
||||||
|
Async file-backed key-value store for **single-process local state** — last-used
|
||||||
|
command, rate-limit timestamps, seen-IDs, simple bot state. Persists forever to a JSON
|
||||||
|
file with atomic writes.
|
||||||
|
|
||||||
|
It is **a KV store, not a cache**: no TTL, no expiry, no eviction. Values live until
|
||||||
|
you `delete` or `clear` them.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
`requirements.txt`:
|
||||||
|
|
||||||
|
```
|
||||||
|
aiokv @ git+ssh://git@git.rethinkstudios.io/rethink-public/aiokv.git@v0.1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Direct:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install "aiokv @ git+ssh://git@git.rethinkstudios.io/rethink-public/aiokv.git@v0.1.0"
|
||||||
|
```
|
||||||
|
|
||||||
|
Requires `aiofiles` (pulled transitively).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```python
|
||||||
|
from aiokv import AioKV
|
||||||
|
|
||||||
|
kv = AioKV("state.json")
|
||||||
|
|
||||||
|
await kv.set("last_command", "ping")
|
||||||
|
await kv.set("seen_ran_cleanup") # value omitted -> stores int(time.time())
|
||||||
|
|
||||||
|
cmd = await kv.get("last_command") # "ping"
|
||||||
|
when = await kv.get("seen_ran_cleanup") # the unix timestamp
|
||||||
|
miss = await kv.get("absent", default=0) # 0
|
||||||
|
|
||||||
|
await kv.delete("last_command") # True (removed or absent)
|
||||||
|
everything = await kv.get_all() # {"seen_ran_cleanup": ...}
|
||||||
|
await kv.clear() # removes the file
|
||||||
|
```
|
||||||
|
|
||||||
|
The timestamp default (`set(key)` with no value) is for "mark that I saw/did X at
|
||||||
|
time T" — the common rate-limit / seen-ID pattern.
|
||||||
|
|
||||||
|
### Back-compat
|
||||||
|
|
||||||
|
This lib was originally `aiocache`. Legacy call sites keep working — `aiocache` is a
|
||||||
|
re-export alias of `AioKV`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from aiokv import aiocache # alias of AioKV; same API
|
||||||
|
kv = aiocache("state.json")
|
||||||
|
```
|
||||||
|
|
||||||
|
Prefer `AioKV` in new code.
|
||||||
|
|
||||||
|
## Durability
|
||||||
|
|
||||||
|
Writes are **atomic**: data is written to a temp file in the same directory and
|
||||||
|
`os.replace()`d over the target (atomic on POSIX). A crash mid-write leaves the
|
||||||
|
previous good file intact, and a reader never observes a partial file. A single
|
||||||
|
`asyncio.Lock` guards every read and write, so concurrent operations on one instance
|
||||||
|
are consistent and no update is lost. All blocking filesystem calls run via
|
||||||
|
`asyncio.to_thread`, so nothing stalls the event loop.
|
||||||
|
|
||||||
|
## Scope — read this
|
||||||
|
|
||||||
|
- **Single-process, single-instance only.** The lock is per-instance. Two `AioKV`
|
||||||
|
instances — or two processes — pointing at the same file are **not** safe; they will
|
||||||
|
clobber each other's writes. For shared cross-process / cross-bot state, that is a
|
||||||
|
database's job (e.g. our `mongo` lib), not aiokv.
|
||||||
|
- **Not a cache.** No TTL/expiry/eviction. If you need entries that age out, this is
|
||||||
|
the wrong tool.
|
||||||
|
|
||||||
|
## Error contract
|
||||||
|
|
||||||
|
- `get` / `set` / `get_all` raise on unexpected I/O (and `_load` raises on a
|
||||||
|
truncated/corrupt file) so a real failure is visible rather than silently masked.
|
||||||
|
- `delete` / `clear` log the exception and return `False` on error, `True` otherwise.
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
Tagged `vX.Y.Z`. Pin the tag in `requirements.txt`.
|
||||||
Loading…
Reference in New Issue
Block a user