167 lines
5.7 KiB
Python
167 lines
5.7 KiB
Python
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
import mlflow.tracing.trace_archival_config as trace_archival_config_module
|
|
from mlflow.environment_variables import MLFLOW_TRACE_ARCHIVAL_CONFIG
|
|
from mlflow.exceptions import MlflowException
|
|
from mlflow.tracing.trace_archival_config import get_trace_archival_server_config
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def reset_trace_archival_server_config_cache(monkeypatch):
|
|
monkeypatch.setattr(trace_archival_config_module, "_TRACE_ARCHIVAL_SERVER_CONFIG_CACHE", None)
|
|
|
|
|
|
def _write_trace_archival_config_lines(tmp_path, lines):
|
|
config_path = tmp_path / "trace-archival.yaml"
|
|
config_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
return config_path
|
|
|
|
|
|
def _write_trace_archival_config(tmp_path, *, retention="30d"):
|
|
archive_path = tmp_path / "archive"
|
|
archive_path.mkdir(exist_ok=True)
|
|
return _write_trace_archival_config_lines(
|
|
tmp_path,
|
|
[
|
|
"trace_archival:",
|
|
" enabled: true",
|
|
f" location: {archive_path.as_uri()}",
|
|
f" retention: {retention}",
|
|
],
|
|
)
|
|
|
|
|
|
def test_get_trace_archival_server_config_rejects_null_location(monkeypatch, tmp_path):
|
|
config_path = _write_trace_archival_config_lines(
|
|
tmp_path,
|
|
[
|
|
"trace_archival:",
|
|
" enabled: true",
|
|
" location:",
|
|
" retention: 30d",
|
|
],
|
|
)
|
|
monkeypatch.setenv(MLFLOW_TRACE_ARCHIVAL_CONFIG.name, str(config_path))
|
|
|
|
with pytest.raises(MlflowException, match="trace_archival.location") as exc_info:
|
|
get_trace_archival_server_config()
|
|
|
|
assert "Expected a URI string." in exc_info.value.message
|
|
|
|
|
|
def test_get_trace_archival_server_config_rejects_local_path_location(monkeypatch, tmp_path):
|
|
config_path = _write_trace_archival_config_lines(
|
|
tmp_path,
|
|
[
|
|
"trace_archival:",
|
|
" enabled: true",
|
|
" location: archive",
|
|
" retention: 30d",
|
|
],
|
|
)
|
|
monkeypatch.setenv(MLFLOW_TRACE_ARCHIVAL_CONFIG.name, str(config_path))
|
|
|
|
with pytest.raises(MlflowException, match="trace_archival.location") as exc_info:
|
|
get_trace_archival_server_config()
|
|
|
|
assert "Expected a URI string." in exc_info.value.message
|
|
|
|
|
|
def test_get_trace_archival_server_config_rejects_null_retention(monkeypatch, tmp_path):
|
|
config_path = _write_trace_archival_config_lines(
|
|
tmp_path,
|
|
[
|
|
"trace_archival:",
|
|
" enabled: true",
|
|
f" location: {(tmp_path / 'archive').as_uri()}",
|
|
" retention:",
|
|
],
|
|
)
|
|
monkeypatch.setenv(MLFLOW_TRACE_ARCHIVAL_CONFIG.name, str(config_path))
|
|
|
|
with pytest.raises(MlflowException, match="trace_archival.retention") as exc_info:
|
|
get_trace_archival_server_config()
|
|
|
|
assert "Expected a duration in the form" in exc_info.value.message
|
|
|
|
|
|
def test_get_trace_archival_server_config_uses_cached_value_before_expiry(monkeypatch, tmp_path):
|
|
config_path = _write_trace_archival_config(tmp_path, retention="30d")
|
|
monkeypatch.setenv(MLFLOW_TRACE_ARCHIVAL_CONFIG.name, str(config_path))
|
|
|
|
with patch.object(trace_archival_config_module.time, "monotonic", side_effect=[10.0, 12.0]):
|
|
first = get_trace_archival_server_config()
|
|
config_path.write_text(
|
|
"\n".join([
|
|
"trace_archival:",
|
|
" enabled: true",
|
|
f" location: {(tmp_path / 'archive').as_uri()}",
|
|
" retention: 7d",
|
|
])
|
|
+ "\n",
|
|
encoding="utf-8",
|
|
)
|
|
second = get_trace_archival_server_config()
|
|
|
|
assert first is not None
|
|
assert second is not None
|
|
assert first.retention == "30d"
|
|
assert second.retention == "30d"
|
|
|
|
|
|
def test_get_trace_archival_server_config_reloads_and_logs_changes(monkeypatch, tmp_path):
|
|
config_path = _write_trace_archival_config(tmp_path, retention="30d")
|
|
monkeypatch.setenv(MLFLOW_TRACE_ARCHIVAL_CONFIG.name, str(config_path))
|
|
|
|
with (
|
|
patch.object(trace_archival_config_module.time, "monotonic", side_effect=[10.0, 16.0]),
|
|
patch.object(trace_archival_config_module, "_logger") as mock_logger,
|
|
):
|
|
first = get_trace_archival_server_config()
|
|
config_path.write_text(
|
|
"\n".join([
|
|
"trace_archival:",
|
|
" enabled: true",
|
|
f" location: {(tmp_path / 'archive').as_uri()}",
|
|
" retention: 7d",
|
|
" max_traces_per_pass: 25",
|
|
])
|
|
+ "\n",
|
|
encoding="utf-8",
|
|
)
|
|
second = get_trace_archival_server_config()
|
|
|
|
assert first is not None
|
|
assert second is not None
|
|
assert first.retention == "30d"
|
|
assert second.retention == "7d"
|
|
assert second.max_traces_per_pass == 25
|
|
mock_logger.info.assert_called_once_with(
|
|
"Trace archival config changed; refreshed cached server settings."
|
|
)
|
|
|
|
|
|
def test_get_trace_archival_server_config_keeps_last_good_value_on_refresh_failure(
|
|
monkeypatch, tmp_path
|
|
):
|
|
config_path = _write_trace_archival_config(tmp_path, retention="30d")
|
|
monkeypatch.setenv(MLFLOW_TRACE_ARCHIVAL_CONFIG.name, str(config_path))
|
|
|
|
with (
|
|
patch.object(trace_archival_config_module.time, "monotonic", side_effect=[10.0, 16.0]),
|
|
patch.object(trace_archival_config_module, "_logger") as mock_logger,
|
|
):
|
|
first = get_trace_archival_server_config()
|
|
config_path.write_text("trace_archival: [\n", encoding="utf-8")
|
|
second = get_trace_archival_server_config()
|
|
|
|
assert first is not None
|
|
assert second is not None
|
|
assert second == first
|
|
mock_logger.warning.assert_called_once_with(
|
|
"Failed to refresh trace archival config; continuing to use the last valid config.",
|
|
exc_info=True,
|
|
)
|