fix(proxy): schedule the cleanup job when a retention db row lands before the side effects run

A config reload applies the db row to the SettingsStore before _update_general_settings snapshots the previous retention values, so the before/after compare saw no change and a retention period first set through /config/update never scheduled the cleanup job. Also reschedule when the job is missing but a retention period is set

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
yucheng 2026-09-23 07:46:04 +00:00
parent 57b27fcfc1
commit 6cb3bc2609
2 changed files with 22 additions and 1 deletions

View file

@ -7630,7 +7630,10 @@ class ProxyConfig:
db_values: Mapping[str, SettingsJsonValue],
previous_retention_values: tuple[SettingsJsonValue | None, ...],
) -> None:
if previous_retention_values != self._resolved_retention_values():
resolved: Final = self._resolved_retention_values()
wants_job: Final = any(value is not None for value in resolved)
has_job: Final = scheduler is not None and scheduler.get_job("spend_log_cleanup_job") is not None
if previous_retention_values != resolved or wants_job != has_job:
await self._reschedule_spend_log_cleanup_job()
async def _apply_ssrf_settings(self, db_values: Mapping[str, SettingsJsonValue]) -> None:

View file

@ -3808,6 +3808,7 @@ async def test_ProxyConfig__reschedule_spend_log_cleanup_job_daily_tag_spend_ret
async def test_ProxyConfig__update_general_settings_updates_daily_tag_spend_retention(monkeypatch):
settings = {}
monkeypatch.setattr("litellm.proxy.proxy_server.general_settings", settings)
monkeypatch.setattr("litellm.proxy.proxy_server.scheduler", None)
pc = ProxyConfig()
reschedule = AsyncMock()
monkeypatch.setattr(pc, "_reschedule_spend_log_cleanup_job", reschedule)
@ -3818,6 +3819,22 @@ async def test_ProxyConfig__update_general_settings_updates_daily_tag_spend_rete
reschedule.assert_awaited_once()
@pytest.mark.asyncio
async def test_ProxyConfig__update_general_settings_schedules_cleanup_when_db_row_was_already_applied(monkeypatch):
"""A config reload applies the db row to the store before the side effects run, so the
before/after snapshot is equal; the job must still be scheduled when none is running."""
fake_scheduler = MagicMock()
fake_scheduler.get_job.return_value = None
monkeypatch.setattr("litellm.proxy.proxy_server.scheduler", fake_scheduler)
monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", None)
pc = ProxyConfig()
pc.settings.apply_db_row("general_settings", {"maximum_daily_tag_spend_retention_period": "90d"})
monkeypatch.setattr("litellm.proxy.proxy_server.general_settings", pc.settings)
await pc._update_general_settings({"maximum_daily_tag_spend_retention_period": "90d"})
assert fake_scheduler.add_job.call_count == 1
assert fake_scheduler.add_job.call_args.kwargs["id"] == "spend_log_cleanup_job"
# ---------------------------------------------------------------------------
# ProxyConfig._update_general_settings
# ---------------------------------------------------------------------------
@ -3949,6 +3966,7 @@ async def test_ProxyConfig__update_general_settings_skips_redundant_retention_re
pc = ProxyConfig()
reschedule: Final = AsyncMock()
monkeypatch.setattr(proxy_server, "general_settings", {})
monkeypatch.setattr(proxy_server, "scheduler", MagicMock())
monkeypatch.setattr(pc, "_reschedule_spend_log_cleanup_job", reschedule)
await pc._update_general_settings({"maximum_health_check_retention_period": "30d"})