Releases History
Chronological timeline of all NimbusKV releases, licensing transitions, downloads, and changelog highlights.
v3.1.1
Latest StableApache 2.0Fixed
- TTL scheduler heap could still grow without bound for a hot key. The 3.1.0 fix below stopped the scheduler from keeping an entry for every key ever written, but
schedule()always pushed a new heap entry without removing the old one for the same key. A key with a long TTL that gets rescheduled often (set(k, v, ttl=...)called repeatedly on one key) left stale entries sitting in the heap until each one's original deadline arrived, so memory still grew in the meantime even though_generationstayed correctly sized. The heap is now compacted once stale entries build up to roughly twice the live count.
Found during code review of v3.1.0 before it was tagged or published. Covered by a new regression test in tests/test_ttl.py (5,000 reschedules of one key; heap stays under 200 entries).
v3.1.0
Apache 2.0License change: NimbusKV is now licensed under the Apache License 2.0 (previously GPL-3.0). You can use it in commercial and closed-source products; redistributions must keep the LICENSE and NOTICE files. Versions up to and including 3.0.1 remain under GPL-3.0 for anyone who already has them. New files: NOTICE, CLA.md (contributor license agreement, required for pull requests), TRADEMARKS.md, COMMERCIAL-LICENSING.md. Every source file now carries an SPDX license header.
Upgrade note: code that writes to the store from inside an atomic() or mutate() function worked in 3.0.1 and now raises ReentrantWriteError. Move the second write after the call, or combine both into one mutate()/mset().
Changed
- Writes now use one short mutex instead of optimistic compare-and-swap with retry. In v3.0.1,
mutate()built the new snapshot outside the lock and retried immediately when another writer got there first. Under a hot key every losing thread threw its work away and tried again, so throughput fell as threads were added. Each write now takes the lock once and does its work once. Readers are unaffected and still never lock. atomic()/mutate()functions now run exactly once per call (they used to re-run on every lost race), while the write lock is held. Writing to the same store from inside one now raises the newReentrantWriteErrorinstead of deadlocking.max_retriesandConflictRetryExceededare kept for compatibility but are no longer used.
Fixed
delete()could report a deletion that did not happen. Two threads deleting the same key could both returnTrueand both fire a"delete"notification. The existence check now happens inside the write, so exactly one wins.- Subscribers could be told the wrong old value.
set(),mset()anddelete()read the old value before the write, so under concurrent writers it could be stale. It is now captured inside the write. - A TTL expiry could delete a value written after it. The scheduler checked "is this TTL still live?", released its lock, then deleted. A
set()in that gap was wiped out. The check now runs inside the store's write lock, andset()cancels the old TTL before writing. - TTL scheduler memory leak. The scheduler kept a generation entry for every key ever written, with or without a TTL, forever. Entries now exist only while a TTL is pending.
atomic()dropped a key's TTL in the persistence backend. It persistedexpire_at=None, so a TTL key touched byatomic()never expired after a restart. It now persists the real pending expiry.- Issue templates were in
.github/ISSSUE_TEMPLATE/(typo), so GitHub ignored them. Moved to.github/ISSUE_TEMPLATE/, root duplicates removed, PR template moved into.github/. - Free-threaded CI checked the GIL before importing NimbusKV, so it missed that importing
immutablesturns the GIL back on. It now checks after the import and runs withPYTHON_GIL=0.
Performance
set()without subscribers or TTL no longer takes the subscriber lock or the TTL scheduler lock._notify()takes no lock at all (subscriber lists are copy-on-write).- Measured with
bench/b06_write_path.pyon a 2-core Linux sandbox (median of 5):
The hot-key collapse in the published 3.0.1 benchmark (439K to 55K ops/s, 16 logical CPUs) could not be reproduced on 2 cores, so 3.1.0's effect on it is not yet measured. Run bench/b06_write_path.py on a many-core machine before claiming it.
Added
tests/test_concurrency.py: 11 regression tests, one per bug above. 8 of them fail on 3.0.1.bench/b06_write_path.py: write-path before/after benchmark.
v3.0.1
GPL-3.0v3.0.0
GPL-3.0Project renamed from livedict to nimbuskv. The import path is now from nimbuskv import NimbusKV (previously from livedict import LiveDict). See the migration note in the README for details. The old livedict package on PyPI will remain published as a deprecated stub pointing here.
Rewrite - new architecture, not a patch on v2.
Added
ReactiveStore: immutable-snapshot, optimistic-concurrency core (lock-free reads always; lock-free writes on free-threaded Python via runtime detection, correct retry-based writes on standard GIL Python).runtime.py: one-time interpreter detection (LOCK_FREE_MODE,gil_enabled(),is_free_threaded_build()).subscribe(): real push-based reactivity (set/delete/expireevents), replacing v2's fire-and-forget callback registration.atomic(): safe compound read-modify-write, replacing manuallock()/unlock().mset()/mget(): batched multi-key atomic write + convenience multi-key read.wait_for(): block a thread until a condition on a key holds, via subscriptions -- no polling.- Context manager support (
with NimbusKV() as ld:). py.typedmarker (PEP 561) for type-checker support.- Genuinely non-blocking async API for the in-memory path (no
run_in_executorused when there's no real I/O). - Persistence backends (
NullBackend/SQLiteBackend/RedisBackend) rebuilt as write-through mirrors with startup rehydration, decoupled from the hot read/write path. - Full docstrings (Google-style, with runnable examples) on every public class and method.
Fixed
ExpirySchedulernow uses a per-key generation counter -- rescheduling a key's TTL correctly invalidates the previous deadline instead of leaving a stale heap entry that could (harmlessly, but wastefully) fire later.- Subscriber callbacks that raise no longer propagate into the write path or the scheduler thread; they're logged via the
nimbuskvlogger instead.
Removed
- Sandbox module - v2's was a timeout wrapper, not real isolation; removed rather than kept under a misleading name.
- Manual
lock()/unlock()- replaced byatomic(). - Fake-async (
run_in_executorwrapping non-I/O calls) for the memory backend.
Breaking
- This is not backwards compatible with v2's API by design (
work_mode=constructor arg removed in favor ofset/asetnaming; callback registration replaced bysubscribe()).
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project follows Semantic Versioning.
Release: [2.0.2] - 2025-10-05 - latest#
Changed#
- The callback handler was calling the callbacks 2 times.I messed up by adding another loop if there is any RunTimeError at _run_async in the _trigger_event function.. fixed it.
Release: [2.0.1] - 2025-10-05#
Changed#
- setup.py had a major requirement issue that was fixed. (I had mistakenly added sqlite3 as a requirement but it's not needed since Python 3.6+ has included it.)
Release: [2.0.0] - 2025-10-04#
Added#
- Comprehensive Google-style docstrings added to modules, classes, and functions.
- Sandbox functionality is now active and maintained within the package.
Changed#
- Project version bumped to
2.0.0. - Packaging files (
setup.py,pyproject.toml) updated for PyPI release.
Removed#
- Cryptography-related code has been removed from the package. Encryption and cryptographic responsibilities are now delegated to the user; the library no longer provides built-in cryptographic primitives.
Notes#
- The
sandboxmodule is live and behaves according to the code in this release; users should review sandbox behavior for their environment and use cases.
Release: [v1.0.4] - 2025-08-11#
Added#
- Pydantic-based configuration models for storage/backends/monitoring and bucket policies.
- New persistence backends:
- SQLite backend with short-lived connections and indexing.
- File-backed object store for external object storage emulation.
- Improved InMemory backend used for testing and simple runs.
- CipherAdapter with AES-GCM (cryptography) and deterministic base64 XOR fallback.
- Heap-based expiry monitor with efficient wake-ups and rebuild heuristics.
- Bucket semantics, bucket policies, and hybrid routing placeholders.
- Better backend fallbacks and tolerant lookup behavior (any-bucket fallback for SQLite/File).
- Expanded API and documentation covering rotation, hooks, and persistence options.
Changed#
- Packaging bumped to
1.0.4-releaseto mark the release-ready core implementation. - README and packaging metadata updated to reflect new dependencies and features.
Fixed#
- Backend expiry handling improved to avoid stale reads.
- sqlite backend uses short-lived sqlite3 connections to avoid file locks on Windows.
- File backend tolerant lookup fixes and safer cleanup logic.
Prior patch notes (v1.0.4 and earlier)#
No previous changes. This is the first version of NimbusKV's official release.