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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions