from __future__ import annotations import logging import time from typing import Any from backend.app.core.config import settings logger = logging.getLogger(__name__) _fallback_mode = False _neo4j_singleton: Any = None class _NoOpDriver: """Minimal stub that satisfies the neo4j.Driver interface used by Orsync Scenarist. Returns empty results so the app can start on platforms without Neo4j. """ def session(self, **kwargs: Any) -> "_NoOpSession": return _NoOpSession() def close(self) -> None: pass def verify_connectivity(self) -> None: pass class _NoOpSession: def __enter__(self) -> "_NoOpSession": return self def __exit__(self, *args: Any) -> None: pass def run(self, query: str, **kwargs: Any) -> "_NoOpResult": return _NoOpResult() def write_transaction(self, func: Any, *args: Any, **kwargs: Any) -> Any: return func(self, *args, **kwargs) def read_transaction(self, func: Any, *args: Any, **kwargs: Any) -> Any: return func(self, *args, **kwargs) def execute_write(self, func: Any, *args: Any, **kwargs: Any) -> Any: return func(self, *args, **kwargs) def execute_read(self, func: Any, *args: Any, **kwargs: Any) -> Any: return func(self, *args, **kwargs) class _NoOpResult: def single(self) -> None: return None def data(self) -> list: return [] def __iter__(self): return iter([]) def get_neo4j_driver(): # type: ignore[return] """Return a Neo4j driver, retrying briefly for an embedded server to start. Falls back to a no-op stub if Neo4j is unreachable after retries. Once connected, the driver is cached as a singleton. """ global _fallback_mode, _neo4j_singleton if _neo4j_singleton is not None: return _neo4j_singleton if _fallback_mode: return _NoOpDriver() from neo4j import GraphDatabase # Retry a few times — the embedded neo4j may still be starting for attempt in range(8): try: driver = GraphDatabase.driver( settings.neo4j_uri, auth=(settings.neo4j_username, settings.neo4j_password), ) driver.verify_connectivity() _neo4j_singleton = driver if attempt > 0: logger.info("Neo4j connected after %d retries", attempt) return driver except Exception: if attempt < 7: time.sleep(1.0) logger.warning( "Neo4j is not reachable at %s — falling back to no-op stub. " "Graph queries will return empty results.", settings.neo4j_uri, ) _fallback_mode = True return _NoOpDriver()