Skip to content

Commit b39cc77

Browse files
authored
feat: Oracle - make connection lazy, add closing methods and refactor tests (#3665)
1 parent ff27d73 commit b39cc77

7 files changed

Lines changed: 262 additions & 118 deletions

File tree

integrations/oracle/src/haystack_integrations/components/retrievers/oracle/embedding_retriever.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ async def run_async(
8484
)
8585
return {"documents": docs}
8686

87+
def close(self) -> None:
88+
"""
89+
Release the synchronous resources of the underlying Document Store.
90+
"""
91+
self.document_store.close()
92+
8793
def to_dict(self) -> dict[str, Any]:
8894
"""
8995
Serializes the component to a dictionary.

integrations/oracle/src/haystack_integrations/components/retrievers/oracle/keyword_retriever.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ async def run_async(
8282
)
8383
return {"documents": docs}
8484

85+
def close(self) -> None:
86+
"""
87+
Release the synchronous resources of the underlying Document Store.
88+
"""
89+
self.document_store.close()
90+
8591
def to_dict(self) -> dict[str, Any]:
8692
"""
8793
Serializes the component to a dictionary.

integrations/oracle/src/haystack_integrations/document_stores/oracle/document_store.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import logging
99
import re
1010
import threading
11+
from contextlib import suppress
1112
from dataclasses import dataclass
1213
from typing import Any, Literal
1314

@@ -177,11 +178,7 @@ def __init__(
177178

178179
self._pool: oracledb.ConnectionPool | None = None
179180
self._pool_lock = threading.Lock()
180-
181-
if create_table_if_not_exists:
182-
self._ensure_table()
183-
if create_index:
184-
self.create_hnsw_index()
181+
self._setup_done = False
185182

186183
def _get_pool(self) -> oracledb.ConnectionPool:
187184
if self._pool is not None:
@@ -212,14 +209,33 @@ def _get_pool(self) -> oracledb.ConnectionPool:
212209
return self._pool
213210

214211
def _get_connection(self) -> oracledb.Connection:
212+
self._ensure_setup()
215213
return self._get_pool().acquire()
216214

217-
def __del__(self) -> None:
215+
def _ensure_setup(self) -> None:
216+
"""
217+
Create the backing table and optional HNSW index once, on first use if needed.
218+
"""
219+
if self._setup_done:
220+
return
221+
self._setup_done = True
222+
try:
223+
if self.create_table_if_not_exists:
224+
self._ensure_table()
225+
if self.create_index:
226+
self.create_hnsw_index()
227+
except Exception:
228+
self._setup_done = False
229+
raise
230+
231+
def close(self) -> None:
232+
"""
233+
Release the associated synchronous resources.
234+
"""
218235
if self._pool is not None:
219-
try:
236+
with suppress(Exception):
220237
self._pool.close()
221-
except Exception:
222-
logger.warning("Failed to close Oracle connection pool during cleanup.", exc_info=True)
238+
self._pool = None
223239

224240
def _ensure_table(self) -> None:
225241
sql = f"""

integrations/oracle/tests/conftest.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,36 +16,41 @@
1616
_DSN = "localhost:1521/freepdb1"
1717

1818

19-
def _make_store(table: str, embedding_dim: int) -> OracleDocumentStore:
20-
return OracleDocumentStore(
21-
connection_config=OracleConnectionConfig(
22-
user=Secret.from_token(_USER),
23-
password=Secret.from_token(_PASSWORD),
24-
dsn=Secret.from_token(_DSN),
25-
),
26-
table_name=table,
27-
embedding_dim=embedding_dim,
28-
distance_metric="COSINE",
29-
create_table_if_not_exists=True,
30-
)
19+
@pytest.fixture
20+
def make_store():
21+
"""Factory for a real OracleDocumentStore. Uses token secrets, so instances are not serialization-safe."""
22+
23+
def _make(table: str, embedding_dim: int = 4, *, create_table_if_not_exists: bool = True) -> OracleDocumentStore:
24+
return OracleDocumentStore(
25+
connection_config=OracleConnectionConfig(
26+
user=Secret.from_token(_USER),
27+
password=Secret.from_token(_PASSWORD),
28+
dsn=Secret.from_token(_DSN),
29+
),
30+
table_name=table,
31+
embedding_dim=embedding_dim,
32+
create_table_if_not_exists=create_table_if_not_exists,
33+
)
34+
35+
return _make
3136

3237

3338
@pytest.fixture
34-
def document_store():
39+
def document_store(make_store):
3540
"""768-dim store required by the mixin's filterable_docs fixture."""
3641
table = f"hs_sync_{uuid.uuid4().hex[:8]}"
37-
s = _make_store(table, embedding_dim=768)
42+
s = make_store(table, 768)
3843
yield s
3944
with s._get_connection() as conn, conn.cursor() as cur:
4045
cur.execute(f"DROP TABLE {table} PURGE")
4146
conn.commit()
4247

4348

4449
@pytest.fixture
45-
def embedding_store():
50+
def embedding_store(make_store):
4651
"""4-dim store for embedding-retrieval, HNSW, and async tests."""
4752
table = f"hs_emb_{uuid.uuid4().hex[:8]}"
48-
s = _make_store(table, embedding_dim=4)
53+
s = make_store(table)
4954
yield s
5055
with s._get_connection() as conn, conn.cursor() as cur:
5156
cur.execute(f"DROP TABLE {table} PURGE")

0 commit comments

Comments
 (0)