From 86b24befc11231ccfada401f31afbf676b06e819 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 13 Aug 2026 03:23:27 +0000 Subject: [PATCH] fix(proxy): stop discarding failed daily spend transactions before requeue Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/proxy/db/db_spend_update_writer.py | 3 -- .../proxy/db/test_db_spend_update_writer.py | 46 +++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/litellm/proxy/db/db_spend_update_writer.py b/litellm/proxy/db/db_spend_update_writer.py index 14604dbc04e..356e45a9daa 100644 --- a/litellm/proxy/db/db_spend_update_writer.py +++ b/litellm/proxy/db/db_spend_update_writer.py @@ -1655,9 +1655,6 @@ class DBSpendUpdateWriter: ) except Exception as e: - if "transactions_to_process" in locals(): - for key in transactions_to_process: - daily_spend_transactions.pop(key, None) _raise_failed_update_spend_exception(e=e, start_time=start_time, proxy_logging_obj=proxy_logging_obj) @staticmethod diff --git a/tests/test_litellm/proxy/db/test_db_spend_update_writer.py b/tests/test_litellm/proxy/db/test_db_spend_update_writer.py index 62bfca73cb4..226cc62858a 100644 --- a/tests/test_litellm/proxy/db/test_db_spend_update_writer.py +++ b/tests/test_litellm/proxy/db/test_db_spend_update_writer.py @@ -1425,6 +1425,52 @@ async def test_update_daily_spend_re_raises_exception_after_logging(): ) +@pytest.mark.asyncio +async def test_update_daily_spend_keeps_failed_transactions_for_retry(): + """ + A failed batch must stay in the caller's transaction dict, otherwise the + Redis re-queue in _commit_spend_updates_to_db_with_redis has nothing left to + push back and the spend is lost permanently. + """ + + def raise_outage(): + raise ValueError("simulated database outage") + + prisma_client = _RecordingPrisma(execute_raw=raise_outage) + + daily_spend_transactions = { + "test_key": { + "user_id": "test-user", + "date": "2024-01-01", + "api_key": "test-api-key", + "model": "gpt-4", + "custom_llm_provider": "openai", + "prompt_tokens": 10, + "completion_tokens": 20, + "spend": 0.1, + "api_requests": 1, + "successful_requests": 1, + "failed_requests": 0, + } + } + expected = dict(daily_spend_transactions) + + mock_proxy_logging = MagicMock() + mock_proxy_logging.failure_handler = AsyncMock() + + with pytest.raises(ValueError, match="simulated database outage"): + await DBSpendUpdateWriter._update_daily_spend( + n_retry_times=0, + prisma_client=prisma_client, + proxy_logging_obj=mock_proxy_logging, + daily_spend_transactions=daily_spend_transactions, + entity_type="user", + entity_id_field="user_id", + ) + + assert daily_spend_transactions == expected + + @pytest.mark.asyncio async def test_commit_key_spend_updates_includes_last_active(): """