Skip to content

Native AsyncClient/AsyncCollection for the 10 backends with async SDKs (Phase 2) #20

Description

@thorwhalen

Native AsyncClient / AsyncCollection for the 10 backends with async SDKs (Phase 2)

PR #19 landed Phase 1 — the universal AsyncClientWrapper / AsyncCollectionWrapper (asyncio.to_thread-based) that makes every backend usable from async code day one. This issue tracks Phase 2: replacing the wrapper with native async implementations for backends whose SDKs ship one.

Native implementations set native_async = True on the class and bypass asyncio.to_thread, doing real non-blocking I/O. Consumers in high-concurrency event-loop apps (FastAPI, Starlette) should prefer them.

The 10 candidates (probed in vd-hybrid-venv)

Backend Async SDK Approx work
chroma chromadb.AsyncClientAPI (chromadb.AsyncHttpClient) Wrap the client into NativeAsyncChromaClient + NativeAsyncChromaCollection; reuse most of the sync adapter's data model.
qdrant qdrant_client.AsyncQdrantClient Async sibling of the existing qdrant adapter; same call shapes, all coroutines.
weaviate weaviate.WeaviateAsyncClient (weaviate.use_async_with_local) Async variant of connect_to_local / connect_to_weaviate_cloud.
elasticsearch elasticsearch.AsyncElasticsearch Identical query/index calls; only the client class differs.
redis redis.asyncio.Redis + redis.asyncio.search Mirror of the existing RediSearch adapter; bytes handling identical.
mongodb pymongo.AsyncMongoClient (or motor.motor_asyncio) Same Atlas Search index logic; await every aggregate/find.
lancedb lancedb.AsyncConnection All native; AsyncTable.search and friends.
milvus pymilvus.AsyncMilvusClient (2.5+) Same hybrid_search call shape with await.
pinecone pinecone.PineconeAsyncio Async client; same upsert/query shape.
turbopuffer httpx-based (already async-capable) Internally httpx — needs AsyncClient swap.

Implementation pattern (per backend)

  1. Add a Native<Backend>AsyncClient and Native<Backend>AsyncCollection class alongside the existing sync adapter (probably in the same module).
  2. Set native_async: bool = True on both classes.
  3. Register the async constructor so vd.connect_async(backend, ...) returns the native class instead of the wrapper.
  4. Add the backend to tests/test_async.py's parametrization (skip-if-unreachable for server backends).

Registration hook

connect_async in vd/asynchronous.py currently always wraps the sync client. Phase 2 will introduce a small registry — e.g. register_async_backend(name, async_client_class) analogous to the sync register_backend — and connect_async will dispatch through it.

Refs

Follow-up to #19, refs #11. Each backend is its own clean piece — separate PRs welcome.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions