Skip to content

[Feat]: Redesign QdrantVectorStore collection registry for multi-process collection management #1525

Description

@edwinyyyu

Is your feature request related to a problem?

Horizontal scalability: multiple MemMachine server processes should be able to work with the same logical collections on one Qdrant cluster. Strict sharding — each collection managed by exactly one process, names partitioned across processes — is what the VectorStore contract requires today ("must be managed by at most one process at a time"), and it works. The restriction itself is the limitation, and it cannot be lifted with the current registry design.

Collection metadata lives in per-namespace <ns>__registry Qdrant collections (dummy-vector points keyed uuid5(name)). Qdrant has no conditional writes, transactions, or unique constraints, so create_collection / open_or_create_collection / delete_collection are non-atomic read-check-write sequences whose only guard is a per-process asyncio lock. The moment two processes manage the same name, the contract's guarantees dissolve:

  1. VectorStoreCollectionAlreadyExistsError stops firing — two concurrent create_collection calls both pass the absence check and last-writer-wins the registry point.
  2. Two open_or_create_collection calls with different configs each create a different native collection (native names are sha256(config) while registry points are keyed by name), one silently wins the registry point, and records written through the losing handle become permanently unreachable — VectorStoreCollectionConfigMismatchError never fires.

So this is not a bug under the current contract; it is the reason the contract has to forbid sharing, and lifting it requires redesigning where the registry lives.

Describe the solution you'd like

Move QdrantVectorStore's collection registry out of Qdrant onto the cross-process config registry primitive (#1524), whose unique-constraint insert is a real compare-and-set:

  • The registry insert becomes the atomic commit point of collection creation (native-collection creation first, registration last, so a crash leaves only an empty config-shared native collection that the next same-config creation adopts).
  • AlreadyExists / ConfigMismatch guarantees then hold across processes sharing the same registry database; the per-process lock remains only as in-process serialization.
  • Native collection naming is unchanged.

Describe alternatives you've considered

  • Keeping the registry inside Qdrant: structurally unfixable — there is no CAS primitive to build atomic create on.
  • Distributed locks/leases around lifecycle operations: operationally heavier, and crash-recovery semantics are worse than an insert that is atomic by construction.

Additional context

Scope: collection lifecycle/metadata management. Registry entries store resolved identity (native collection name, plus a generation-scoped partition key), which also makes delete_collection safe against handles held in other processes: writes through a held handle land under the dead generation — invisible, never resurrected by a re-creation. Read-after-write visibility tuning on the data path remains a separate concern.

Companion redesign pieces: strict create/open lifecycle (open-or-create removal) and a declared per-instance concurrency scope (PROCESS/MACHINE/CLUSTER) replacing the ABC's blanket one-process-per-collection sentence, so the capability is expressible through the contract.


🤖 Written by Claude Code (Opus 5) on behalf of @edwinyyyu.

Activity

  1. changed the title [-][Bug]: QdrantVectorStore collection lifecycle races across processes[/-] [+][Feat]: Redesign QdrantVectorStore collection registry for multi-process collection management[/+] on Aug 26, 2026
  2. edwinyyyu commented on Sep 16, 2026

    @edwinyyyu
    ContributorAuthor

    #1656 (vector store chain, slice 11) does what this issue asks in the one-collection shape the chain adopted: the Qdrant (and Milvus) partition registry moves out of the backend into SQL tables in the deployment's relational database, keyed by (collection, partition_key) with a per-life incarnation, and a purge queue beside it; the unique-constraint insert is the create arbiter, the per-process lock table is deleted. No generic ConfigRegistry primitive (#1524): one module, common/vector_store/sql_partition_registry.py, shared by the two stores.

  3. edwinyyyu commented on Sep 16, 2026

    @edwinyyyu
    ContributorAuthor

    Update: #1656 was folded into #1631 (vector store chain, slice 10); the SQL registry described above lives there.

  4. added
    horizontal scalingWrong or unsafe when more than one server process serves the same backends (replicas or workers)
    on Sep 29, 2026
  5. edwinyyyu commented on Oct 2, 2026

    @edwinyyyu
    ContributorAuthor

    Addressed by #1734 (SQL collection registry: create by primary key, an incarnation per collection life, purge claimed with SKIP LOCKED) with the Qdrant move in #1735 and the Milvus move in #1736. Close when the chain merges. Umbrella: #1574; session-layer follow-up: #1755.


    🤖 Written by Claude Code (Claude Fable 5.1) on behalf of @edwinyyyu.

  6. edwinyyyu commented on Oct 3, 2026

    @edwinyyyu
    ContributorAuthor

    Closed as a duplicate of #1524: both ask for a cross-process collection registry, this one for Qdrant specifically. #1734 (registry) and #1735 (Qdrant move) deliver it; #1524 tracks the close on merge.


    🤖 Written by Claude Code (Claude Fable 5.1) on behalf of @edwinyyyu.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    horizontal scalingWrong or unsafe when more than one server process serves the same backends (replicas or workers)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions