diff --git a/litellm/repositories/alert_repository.py b/litellm/repositories/alert_repository.py index a62a9924a0b..299b1a67bdc 100644 --- a/litellm/repositories/alert_repository.py +++ b/litellm/repositories/alert_repository.py @@ -28,8 +28,9 @@ async def record_alerts(transaction: AlertTransaction, detections: Sequence[Aler return await transaction.execute_raw( """INSERT INTO "LiteLLM_Alert" - (detection_key, type, resource_type, resource_id, window_start, window_end, evidence) - SELECT detection_key, type, resource_type, resource_id, window_start, window_end, evidence + (detection_key, type, resource_type, resource_id, window_start, window_end, evidence, created_at, updated_at) + SELECT detection_key, type, resource_type, resource_id, window_start, window_end, evidence, + now() AT TIME ZONE 'UTC', now() AT TIME ZONE 'UTC' FROM jsonb_to_recordset($1::jsonb) AS rows(detection_key text, type text, resource_type text, resource_id text, window_start timestamp, window_end timestamp, evidence jsonb) ON CONFLICT (detection_key) DO NOTHING""", diff --git a/litellm/repositories/performance_alert_repository.py b/litellm/repositories/performance_alert_repository.py index 2c85a4b90f7..b312f6951de 100644 --- a/litellm/repositories/performance_alert_repository.py +++ b/litellm/repositories/performance_alert_repository.py @@ -149,13 +149,14 @@ _LIMITS: Final = f"""SELECT set_config('statement_timeout', '{_STATEMENT_TIMEOUT set_config('lock_timeout', '100ms', true), set_config('work_mem', '1MB', true), set_config('max_parallel_workers_per_gather', '0', true)""" _CLAIM: Final = """INSERT INTO "LiteLLM_PerformanceAlertState" (id, owner, next_run_at, updated_at) -VALUES (1, $1, now() + $2::interval, now()) -ON CONFLICT (id) DO UPDATE SET owner = EXCLUDED.owner, next_run_at = EXCLUDED.next_run_at, updated_at = now() +VALUES (1, $1, now() + $2::interval, now() AT TIME ZONE 'UTC') +ON CONFLICT (id) DO UPDATE SET owner = EXCLUDED.owner, next_run_at = EXCLUDED.next_run_at, + updated_at = now() AT TIME ZONE 'UTC' WHERE "LiteLLM_PerformanceAlertState".next_run_at <= now() + interval '30 seconds' RETURNING day, scope, resume_after AS cursor, wrapped, checked, skipped, completed_at IS NOT NULL AS completed""" _SAVE: Final = """UPDATE "LiteLLM_PerformanceAlertState" SET day = $2, scope = $3, resume_after = $4, wrapped = $5, checked = $6, skipped = $7, - completed_at = CASE WHEN $8 THEN now() END, updated_at = now() + completed_at = CASE WHEN $8 THEN now() AT TIME ZONE 'UTC' END, updated_at = now() AT TIME ZONE 'UTC' WHERE id = 1 AND owner = $1""" _ACCOUNTING_PENDING: Final = """SELECT 1 AS resource_id FROM "LiteLLM_SpendLogDelivery" WHERE state = 'pending' AND created_at < $1::timestamp LIMIT 1""" diff --git a/tests/integration/database/test_performance_alerts.py b/tests/integration/database/test_performance_alerts.py index 0b400634d7d..735f135065c 100644 --- a/tests/integration/database/test_performance_alerts.py +++ b/tests/integration/database/test_performance_alerts.py @@ -158,6 +158,12 @@ async def test_a_full_sweep_saves_one_alert_per_real_change_and_nothing_for_norm await fixture.sweep() + (age,) = read_rows( + "SELECT max(abs(extract(epoch FROM created_at - (now() AT TIME ZONE 'UTC'))))::int AS seconds" + ' FROM "LiteLLM_Alert" WHERE resource_id LIKE %s', + (f"{p}%",), + ) + assert int(str(age["seconds"])) < 300 assert fixture.saved() == { (f"{p}-team-spike", "spend"): ("team", "open"), (f"{p}-user-stopped", "traffic"): ("user", "open"),