fix(qdrant): use collection_exists for startup probe (multi-tenant safe)
The startup path in `get_qdrant_client()` calls `get_collections()` to
check whether the configured collection already exists. That's a
cluster-wide list operation; in managed multi-tenant Qdrant Cloud
deployments where each tenant's JWT is scoped to a single collection
(by design — `access: [{"collection": "tenant_<id>", "access": "rw"}]`),
the call returns `403 Forbidden` and the FastAPI lifespan crashes:
qdrant_client.http.exceptions.UnexpectedResponse: 403 (Forbidden)
raw response: {"error":"forbidden"}
RuntimeError: Cannot start vector sync - Qdrant initialization failed
Switching to `collection_exists(collection_name)` (per-collection
HEAD-style probe) only requires access to the named collection, which
the tenant JWT has. Single-tenant deployments using an admin/master
key are unaffected — they had access to both forms; this picks the
narrower one.
Doesn't change creation semantics: when the collection isn't present
the code path still calls `create_collection`. In a managed setup
where the collection is pre-provisioned by an external admin (e.g.,
the Astrolabe Cloud control plane's create-tenant workflow), that
branch never fires for an existing tenant; cold-start tenants get
their collection created by the workflow before the Pod boots.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
6b22d1ff76
commit
f2d4982b2f
@@ -71,12 +71,21 @@ async def get_qdrant_client() -> AsyncQdrantClient:
|
||||
|
||||
expected_dimension = embedding_service.get_dimension()
|
||||
|
||||
# Explicitly check if collection exists
|
||||
# Explicitly check if collection exists.
|
||||
#
|
||||
# Use `collection_exists(name)` (per-collection HEAD-style probe)
|
||||
# rather than `get_collections()` (cluster-wide list). In managed
|
||||
# multi-tenant Qdrant Cloud setups, per-tenant JWTs are scoped
|
||||
# to a single collection and `get_collections()` returns 403
|
||||
# Forbidden by design — listing other tenants' collections would
|
||||
# be a security regression. `collection_exists` only requires
|
||||
# access to the named collection, which the tenant JWT has.
|
||||
logger.debug(f"Checking if collection '{collection_name}' exists...")
|
||||
collections = await _qdrant_client.get_collections()
|
||||
collection_names = [c.name for c in collections.collections]
|
||||
collection_present = await _qdrant_client.collection_exists(
|
||||
collection_name=collection_name
|
||||
)
|
||||
|
||||
if collection_name in collection_names:
|
||||
if collection_present:
|
||||
# Collection exists - validate dimensions
|
||||
logger.debug(
|
||||
f"Collection '{collection_name}' found, validating dimensions..."
|
||||
|
||||
Reference in New Issue
Block a user