What happened
QdrantVectorStore._name_locks is a WeakKeyDictionary keyed by client, with a comment noting that locks are collected when the client is:
# Keyed by client so locks are garbage-collected when the client is.
_name_locks: ClassVar[
WeakKeyDictionary[AsyncQdrantClient, defaultdict[tuple[str, str], asyncio.Lock]]
] = WeakKeyDictionary()
The outer mapping is indeed weak, but the inner defaultdict is not bounded. It gains an asyncio.Lock for every (namespace, name) the process ever touches, and never drops one. Since a logical collection is a session, a long-running server accumulates one lock object per session for the lifetime of the client — which in practice is the lifetime of the process.
Each lock is small, so this is a slow leak rather than an urgent one, but it is unbounded in the number of sessions served and is invisible because the comment reads as though the lifetime question is already handled.
Expected
Per-collection lock state should not grow without bound over a process's lifetime.
Suggested direction
The locks only serialise lifecycle operations within one process. Once collection metadata is arbitrated by a cross-process registry (#1526, #1527), their role is narrower still. Options, roughly in order of preference: drop them where the registry now provides the arbitration, evict the entry after a successful lifecycle operation, or keep them in a bounded cache.
What happened
QdrantVectorStore._name_locksis aWeakKeyDictionarykeyed by client, with a comment noting that locks are collected when the client is:The outer mapping is indeed weak, but the inner
defaultdictis not bounded. It gains anasyncio.Lockfor every(namespace, name)the process ever touches, and never drops one. Since a logical collection is a session, a long-running server accumulates one lock object per session for the lifetime of the client — which in practice is the lifetime of the process.Each lock is small, so this is a slow leak rather than an urgent one, but it is unbounded in the number of sessions served and is invisible because the comment reads as though the lifetime question is already handled.
Expected
Per-collection lock state should not grow without bound over a process's lifetime.
Suggested direction
The locks only serialise lifecycle operations within one process. Once collection metadata is arbitrated by a cross-process registry (#1526, #1527), their role is narrower still. Options, roughly in order of preference: drop them where the registry now provides the arbitration, evict the entry after a successful lifecycle operation, or keep them in a bounded cache.