Files
2026-07-13 13:22:34 +08:00

109 lines
3.2 KiB
Python

import shutil
from pathlib import Path
import pytest
import sqlalchemy
from mlflow.environment_variables import MLFLOW_TRACKING_URI
from mlflow.store.db.db_types import MSSQL, MYSQL, POSTGRES, SQLITE
from mlflow.store.tracking.dbmodels.models import (
SqlDataset,
SqlEntityAssociation,
SqlEvaluationDataset,
SqlEvaluationDatasetRecord,
SqlExperiment,
SqlExperimentTag,
SqlInput,
SqlInputTag,
SqlLatestMetric,
SqlLoggedModel,
SqlLoggedModelMetric,
SqlLoggedModelParam,
SqlLoggedModelTag,
SqlMetric,
SqlOnlineScoringConfig,
SqlParam,
SqlRun,
SqlTag,
SqlTraceInfo,
SqlTraceMetadata,
SqlTraceTag,
)
from mlflow.store.tracking.sqlalchemy_store import SqlAlchemyStore
ARTIFACT_URI = "artifact_folder"
def _get_query_to_reset_experiment_id(store: SqlAlchemyStore):
dialect = store._get_dialect()
if dialect == POSTGRES:
return "ALTER SEQUENCE experiments_experiment_id_seq RESTART WITH 1"
elif dialect == MYSQL:
return "ALTER TABLE experiments AUTO_INCREMENT = 1"
elif dialect == MSSQL:
return "DBCC CHECKIDENT (experiments, RESEED, 0)"
elif dialect == SQLITE:
# In SQLite, deleting all experiments resets experiment_id
return None
raise ValueError(f"Invalid dialect: {dialect}")
def _cleanup_database(store: SqlAlchemyStore):
with store.ManagedSessionMaker() as session:
# Delete all rows in all tables
for model in (
SqlLoggedModel,
SqlLoggedModelMetric,
SqlLoggedModelParam,
SqlLoggedModelTag,
SqlParam,
SqlMetric,
SqlLatestMetric,
SqlTag,
SqlInputTag,
SqlInput,
SqlDataset,
SqlRun,
SqlTraceTag,
SqlTraceMetadata,
SqlTraceInfo,
SqlEvaluationDatasetRecord,
SqlEntityAssociation,
SqlEvaluationDataset,
SqlExperimentTag,
SqlOnlineScoringConfig,
SqlExperiment,
):
session.query(model).delete()
# Reset experiment_id to start at 1
if reset_experiment_id := _get_query_to_reset_experiment_id(store):
session.execute(sqlalchemy.sql.text(reset_experiment_id))
@pytest.fixture(scope="module")
def cached_db(tmp_path_factory) -> Path:
"""Creates and caches a SQLite database to avoid repeated migrations for each test run."""
tmp_path = tmp_path_factory.mktemp("sqlite_db")
db_path = tmp_path / "mlflow.db"
db_uri = f"sqlite:///{db_path}"
store = SqlAlchemyStore(db_uri, ARTIFACT_URI)
store.engine.dispose()
return db_path
@pytest.fixture
def store(tmp_path: Path, cached_db: Path) -> SqlAlchemyStore:
artifact_uri = tmp_path / "artifacts"
artifact_uri.mkdir(exist_ok=True)
if db_uri_env := MLFLOW_TRACKING_URI.get():
s = SqlAlchemyStore(db_uri_env, artifact_uri.as_uri())
yield s
_cleanup_database(s)
else:
db_path = tmp_path / "mlflow.db"
shutil.copy(cached_db, db_path)
db_uri = f"sqlite:///{db_path}"
s = SqlAlchemyStore(db_uri, artifact_uri.as_uri())
yield s