litellm/ui
Yassin Kortam f94bee8d4f perf(spend-logs): bound retention cleanup so one run cannot saturate the database
Retention cleanup could delete up to 500,000 rows per table per run with no
wall-clock bound and no per-statement timeout, so a large backlog turned into a
single long, lock-holding run.

A run now carries a wall-clock budget shared across every table it prunes, each
delete batch runs under a Postgres statement_timeout and lock_timeout, and batch
size, batch cap, budget and timeout are all operator-configurable from
general_settings and the admin dashboard. Prometheus metrics report rows deleted,
batch duration, remaining eligible rows, batch failures, and why each run ended.

The budget is a hard bound rather than an advisory one. Every statement is issued
with its timeout clamped to the budget that is left, so a batch started just under
the deadline is cancelled at the deadline instead of running a further batch
timeout past it, and no statement is issued once the budget is spent. Measured
against a real Postgres with a deliberately slow delete, a 5s budget ran 20.8s
before and 5.0s after. Partition maintenance is DDL that cannot be cut short, so
it is only started while budget remains

Those four bounds also reach the running job. The scheduler holds one long-lived
SpendLogCleanup, so they are re-read at the start of every run rather than
captured at construction, and the runtime config reload copies them into
general_settings, which it previously did only for the retention period itself.
Clearing one from the dashboard restores its shipped default, while a bound set
in config.yaml is left alone

Partition maintenance is bounded too. Every statement it issues, the two catalog
lookups as well as the CREATE and DROP, carries a timeout, and the DDL also carries
a lock_timeout so a DROP that cannot take its ACCESS EXCLUSIVE lock gives up rather
than queueing behind a long-running reader for as long as that reader lives. The
bound is injected as a callable rather than a number, so it is re-read before every
statement and a loop stops once the budget is gone instead of issuing one statement
per partition against a bound taken before the first of them

Clearing a dashboard override of a bound that config.yaml also declares now
restores the YAML value rather than leaving the deleted override in memory

Saving the form no longer asks the proxy to clear a field that was never stored.
That request answers 400 when no general_settings row exists, which is every
deployment that has not saved these settings, and it aborted the whole save

Resolves LIT-5439
2026-08-13 13:39:26 -07:00
..
litellm-dashboard perf(spend-logs): bound retention cleanup so one run cannot saturate the database 2026-08-13 13:39:26 -07:00
Dockerfile chore(build): move the Admin UI toolchain to Node 24 (#35801) 2026-08-04 12:36:07 -07:00
nginx.conf fix(ui): serve /ui/assets from the nginx image instead of SPA fallback (#34066) 2026-07-21 09:22:02 -07:00