feat: réutiliser le DuckDB existant si le bootstrap échoue (#78)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -118,13 +118,24 @@ def _ensure_database() -> Path:
|
||||
db_path = Path(os.getenv("DUCKDB_PATH", "./decp.duckdb"))
|
||||
parquet_path = os.getenv("DATA_FILE_PARQUET_PATH", "")
|
||||
lock_path = db_path.with_suffix(".duckdb.lock")
|
||||
db_exists = db_path.exists()
|
||||
|
||||
with open(lock_path, "w") as lock_fd:
|
||||
fcntl.flock(lock_fd, fcntl.LOCK_EX)
|
||||
if should_rebuild(db_path, parquet_path):
|
||||
build_database(db_path)
|
||||
else:
|
||||
logger.debug("Base de données déjà disponible et à jour.")
|
||||
try:
|
||||
if should_rebuild(db_path, parquet_path):
|
||||
build_database(db_path)
|
||||
else:
|
||||
logger.debug("Base de données déjà disponible et à jour.")
|
||||
except Exception as e:
|
||||
if db_exists:
|
||||
logger.error(
|
||||
f"Bootstrap données KO ({e}). "
|
||||
f"Réutilisation du DuckDB existant : {db_path}"
|
||||
)
|
||||
else:
|
||||
logger.critical("Aucune base DuckDB et reconstruction impossible.")
|
||||
raise
|
||||
return db_path
|
||||
|
||||
|
||||
|
||||
@@ -312,3 +312,59 @@ def test_concurrent_build_serialized(tmp_path, monkeypatch):
|
||||
assert errors == []
|
||||
assert db_path.exists()
|
||||
assert not tmp_path_artifact.exists()
|
||||
|
||||
|
||||
def _raise(*args, **kwargs):
|
||||
raise RuntimeError("boom")
|
||||
|
||||
|
||||
def test_ensure_database_reuses_db_when_should_rebuild_raises(tmp_path, monkeypatch):
|
||||
import src.db as db
|
||||
|
||||
dbf = tmp_path / "decp.duckdb"
|
||||
dbf.write_bytes(b"existing")
|
||||
monkeypatch.setenv("DUCKDB_PATH", str(dbf))
|
||||
monkeypatch.setenv("DATA_FILE_PARQUET_PATH", "http://unreachable")
|
||||
monkeypatch.setattr(db, "should_rebuild", _raise)
|
||||
result = db._ensure_database() # ne doit pas lever
|
||||
assert result == dbf
|
||||
assert dbf.read_bytes() == b"existing"
|
||||
|
||||
|
||||
def test_ensure_database_reuses_db_when_build_raises(tmp_path, monkeypatch):
|
||||
import src.db as db
|
||||
|
||||
dbf = tmp_path / "decp.duckdb"
|
||||
dbf.write_bytes(b"existing")
|
||||
monkeypatch.setenv("DUCKDB_PATH", str(dbf))
|
||||
monkeypatch.setenv("DATA_FILE_PARQUET_PATH", "http://unreachable")
|
||||
monkeypatch.setattr(db, "should_rebuild", lambda *a, **k: True)
|
||||
monkeypatch.setattr(db, "build_database", _raise)
|
||||
db._ensure_database() # ne doit pas lever
|
||||
assert dbf.read_bytes() == b"existing"
|
||||
|
||||
|
||||
def test_ensure_database_raises_on_cold_start(tmp_path, monkeypatch):
|
||||
import src.db as db
|
||||
|
||||
dbf = tmp_path / "decp.duckdb" # n'existe pas
|
||||
monkeypatch.setenv("DUCKDB_PATH", str(dbf))
|
||||
monkeypatch.setenv("DATA_FILE_PARQUET_PATH", "http://unreachable")
|
||||
monkeypatch.setattr(db, "should_rebuild", lambda *a, **k: True)
|
||||
monkeypatch.setattr(db, "build_database", _raise)
|
||||
with pytest.raises(RuntimeError):
|
||||
db._ensure_database()
|
||||
|
||||
|
||||
def test_ensure_database_builds_when_needed(tmp_path, monkeypatch):
|
||||
import src.db as db
|
||||
|
||||
dbf = tmp_path / "decp.duckdb"
|
||||
dbf.write_bytes(b"old")
|
||||
monkeypatch.setenv("DUCKDB_PATH", str(dbf))
|
||||
monkeypatch.setenv("DATA_FILE_PARQUET_PATH", "http://x")
|
||||
called = {}
|
||||
monkeypatch.setattr(db, "should_rebuild", lambda *a, **k: True)
|
||||
monkeypatch.setattr(db, "build_database", lambda p: called.setdefault("built", p))
|
||||
db._ensure_database()
|
||||
assert called.get("built") == dbf
|
||||
|
||||
Reference in New Issue
Block a user