Manages the Neo4j database driver as a singleton to ensure only one connection pool is created and shared across the application. This pattern is crucial for performance and resource management in a multi-threaded or asynchronous application.
| 48 | self._driver.close() |
| 49 | |
| 50 | class DatabaseManager: |
| 51 | """ |
| 52 | Manages the Neo4j database driver as a singleton to ensure only one |
| 53 | connection pool is created and shared across the application. |
| 54 | |
| 55 | This pattern is crucial for performance and resource management in a |
| 56 | multi-threaded or asynchronous application. |
| 57 | """ |
| 58 | _instance = None |
| 59 | _driver: Optional[Driver] = None |
| 60 | _lock = threading.Lock() # Lock to ensure thread-safe initialization. |
| 61 | |
| 62 | def __new__(cls): |
| 63 | """Standard singleton pattern implementation.""" |
| 64 | if cls._instance is None: |
| 65 | with cls._lock: |
| 66 | # Double-check locking to prevent race conditions. |
| 67 | if cls._instance is None: |
| 68 | cls._instance = super(DatabaseManager, cls).__new__(cls) |
| 69 | return cls._instance |
| 70 | |
| 71 | def __init__(self): |
| 72 | """ |
| 73 | Initializes the manager by reading credentials from environment variables. |
| 74 | The `_initialized` flag prevents re-initialization on subsequent calls. |
| 75 | """ |
| 76 | if hasattr(self, '_initialized'): |
| 77 | return |
| 78 | |
| 79 | self.neo4j_uri = os.getenv('NEO4J_URI') |
| 80 | self.neo4j_username = os.getenv('NEO4J_USERNAME', 'neo4j') |
| 81 | self.neo4j_password = os.getenv('NEO4J_PASSWORD') |
| 82 | self.neo4j_database = os.getenv('NEO4J_DATABASE') # Optional, if not set, will use default database configured in Neo4j |
| 83 | self._initialized = True |
| 84 | |
| 85 | def get_driver(self, graph_name: str = None) -> Driver: |
| 86 | """ |
| 87 | Gets the Neo4j driver instance, creating it if it doesn't exist. |
| 88 | This method is thread-safe. |
| 89 | |
| 90 | The `graph_name` parameter is accepted for interface parity with |
| 91 | FalkorDB (which supports multiple graphs per instance); Neo4j |
| 92 | selects the database via NEO4J_DATABASE, so the argument is ignored. |
| 93 | |
| 94 | Raises: |
| 95 | ValueError: If Neo4j credentials are not set in environment variables. |
| 96 | |
| 97 | Returns: |
| 98 | The a wrapper for Neo4j Driver instance. |
| 99 | """ |
| 100 | if self._driver is None: |
| 101 | with self._lock: |
| 102 | if self._driver is None: |
| 103 | # Ensure all necessary credentials are provided. |
| 104 | missing = self.get_missing_credentials( |
| 105 | self.neo4j_uri, |
| 106 | self.neo4j_username, |
| 107 | self.neo4j_password, |
no outgoing calls