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:
Colin Maudry
2026-06-12 13:48:32 +02:00
parent 5a6aa31c86
commit bba53d81e1
2 changed files with 71 additions and 4 deletions
+15 -4
View File
@@ -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
+56
View File
@@ -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