Release 2.7.0 - #19
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release 2.7.0. Four themes, each with tests; full changelog in
CHANGELOG.md.The async API now mirrors the sync one
The async surface had drifted: eight collection methods and four database
methods had no async counterpart, and the sub-namespaces were flattened into
names that no longer matched (
collection.ttl.sweep()becamesweep_ttl()).Async sub-namespaces are now generic proxies over the sync ones, so a method
added to a sync namespace is reachable from async immediately, with no wrapper
to write.
Added
collection.edges/.events/.ttl/.pending/.maintenance/.countersonAsyncVectorCollection,async with collection.tx(),AsyncVectorDB.transaction(fn),save,as_langchain,as_llama_index,add_texts_streaming, and a real async-generatorevents.subscribe.A parity test fails if a public sync method gains no async counterpart.
Fixed: cancelling an
async withtransaction wedged the database — teardownran through an
await, and suspending while aGeneratorExitis in flightleft the savepoint open and the DB lock held for the life of the process.
One connection per thread
A single shared
sqlite3.Connectionserved every collection and thread.SQLite provides no isolation between operations on one connection, so a read
on one thread could observe another thread's uncommitted rows.
File-backed databases now open one connection per thread. In-memory databases
keep a single shared connection: pooling one requires a shared-cache URI, and
shared cache takes table-level write locks that
busy_timeoutdoes not waitout.
The database-wide lock stands down when connections are pooled — a read on
another thread returned in 1 ms against a transaction held open for 400 ms,
and still saw committed state. Transactions now begin
BEGIN IMMEDIATE, and arolled-back transaction ends its enclosing transaction as well as its
savepoint.
Crash and concurrency fixes
add()reloaded a
view=Trueindex as writable;remove()did not, and usearchcrashes rather than raising on a read-only mapping.
_index is Nonethen re-read the attribute; they now snapshot it once.re-executed statements that already applied and duplicated auto-id rows.
close()anddelete_collection()stop TTL sweepers, which previouslyoutlived both and logged a failure every interval.
usearch>=2.24.0— earlier versions can underflowIndex::size()underconcurrent add/remove (Bug: Integer underflow in Index::size() during concurrent add/remove. unum-cloud/USearch#697).
Transactions cover the vector index
tx()anddb.transaction()guarded only the catalog: a SAVEPOINT rolled therows back while the vectors stayed, leaving the index keyed to rows that no
longer existed.
add_texts,delete_by_ids,pending.flush()andttl.sweep()now buffer their HNSW mutations and apply them just before theoutermost savepoint releases.
Breaking:
add_texts(ids=…)refuses an id that already exists instead ofupserting silently. Pass
on_conflict="replace"for the old behaviour. Thecheck runs before anything is written. The LangChain adapter keeps upserting
so its own contract holds.
Added
collection.reserve_ids(n)andAsyncVectorCollection.atomic(fn).Changed: a search inside a transaction does not see that transaction's own
vector writes — they land at commit. Catalog reads are unaffected.
Verification
ruff-format,ruff,mypy(99 files),bandit,pytest— 872 passed,3 skipped.