Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions methods/EverCore/src/core/lifespan/milvus_lifespan.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Milvus lifespan provider implementation
"""

import os
from collections import defaultdict
from fastapi import FastAPI
from typing import Any
Expand Down Expand Up @@ -41,6 +42,18 @@ async def startup(self, app: FastAPI) -> Any:
Returns:
Any: Milvus client information
"""
# Symmetric env-gate (mirrors QdrantLifespanProvider): when running with
# the Qdrant adapter as the active vector backend, the Milvus lifespan
# must be a no-op. Without this gate it always tries to connect to
# Milvus and crashes the service when Milvus is offline (e.g. after
# cutover or during outage), preventing the Qdrant adapter — which
# runs at order=19, AFTER milvus order=18 — from ever starting.
if os.getenv("VECTOR_STORE_BACKEND", "milvus").strip().lower() == "qdrant":
logger.info(
"VECTOR_STORE_BACKEND=qdrant — Milvus lifespan startup no-op"
)
return None

logger.info("Initializing Milvus connection...")

try:
Expand Down
15 changes: 14 additions & 1 deletion methods/EverCore/src/core/oxm/milvus/base_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,23 @@ def __init__(self, model: Type[T]):
"""
self.model = model
self.model_name = model.__name__
self.collection: Optional[AsyncCollection] = model.async_collection()
# Lazy collection resolution: the underlying Milvus collection is only
# available once `MilvusLifespanProvider` has run `ensure_loaded()`.
# When VECTOR_STORE_BACKEND=qdrant the Milvus lifespan is a no-op, so
# eager resolution here would crash the entire app startup at every
# repository `__init__`. We defer to first access of `self.collection`
# so the app boots and qdrant-mode never touches the Milvus pathway.
self._collection_cache: Optional[AsyncCollection] = None
self.schema = model._SCHEMA
self.all_output_fields = [field.name for field in self.schema.fields]

@property
def collection(self) -> AsyncCollection:
"""Lazy accessor for the underlying async Milvus collection."""
if self._collection_cache is None:
self._collection_cache = self.model.async_collection()
return self._collection_cache

# ==================== Basic CRUD Operations ====================

async def insert(self, entity: T, flush: bool = False) -> str:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def get_tenant_qdrant_config() -> Optional[Dict[str, Any]]:
oder ``None`` falls kein Tenant aktiv.
"""
# Lazy import vermeidet Circular-Dependency bei Adapter-Discovery-Time.
from core.tenants.tenantize.tenant_context import get_current_tenant
from core.tenants.tenant_contextvar import get_current_tenant

# Fail-closed: an unexpected error during tenant resolution must not
# degrade silently to the shared base-prefix path — that would route a
Expand Down
Loading