mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-29 06:33:28 +00:00
fix(proxy): stamp reload timestamps at the precision they are stored at
Postgres stores these columns as TIMESTAMP(3) while Python stamps microseconds, so a pod comparing its in-memory clock against the persisted copy of the same instant read as newer and skipped the reload request it had just recorded. Truncate every stamp to milliseconds at the source, and floor the boot seed the same way, so the in-memory value and its persisted copy compare exactly.
This commit is contained in:
parent
2887eb3322
commit
a7a0c22356
3 changed files with 43 additions and 2 deletions
|
|
@ -80,8 +80,17 @@ class _IntervalConfig(BaseModel):
|
|||
interval_hours: int | None = None
|
||||
|
||||
|
||||
def to_db_precision(value: datetime) -> datetime:
|
||||
"""
|
||||
Drop sub-millisecond digits, matching the TIMESTAMP(3) columns these values round-trip
|
||||
through. Comparing a microsecond-precision stamp against its own truncated copy would
|
||||
read as newer and skip the request it recorded
|
||||
"""
|
||||
return value.replace(microsecond=(value.microsecond // 1000) * 1000)
|
||||
|
||||
|
||||
def utc_now() -> datetime:
|
||||
return datetime.now(timezone.utc)
|
||||
return to_db_precision(datetime.now(timezone.utc))
|
||||
|
||||
|
||||
def _parse_interval_hours(param_value: object) -> int | None:
|
||||
|
|
|
|||
|
|
@ -333,6 +333,7 @@ from litellm.proxy.common_utils.periodic_reload_schedule import (
|
|||
record_manual_reload,
|
||||
record_reload_run,
|
||||
reload_schedule_status,
|
||||
to_db_precision,
|
||||
utc_now,
|
||||
write_reload_interval,
|
||||
)
|
||||
|
|
@ -3862,7 +3863,10 @@ class ProxyConfig:
|
|||
get_model_cost_map_loaded_at,
|
||||
)
|
||||
|
||||
self.model_cost_map_loaded_at: datetime = get_model_cost_map_loaded_at() or utc_now()
|
||||
cost_map_loaded_at = get_model_cost_map_loaded_at()
|
||||
self.model_cost_map_loaded_at: datetime = (
|
||||
to_db_precision(cost_map_loaded_at) if cost_map_loaded_at is not None else utc_now()
|
||||
)
|
||||
|
||||
def is_yaml(self, config_file_path: str) -> bool:
|
||||
if not os.path.isfile(config_file_path):
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ from litellm.proxy.common_utils.periodic_reload_schedule import (
|
|||
record_manual_reload,
|
||||
record_reload_run,
|
||||
reload_schedule_status,
|
||||
to_db_precision,
|
||||
utc_now,
|
||||
write_reload_interval,
|
||||
)
|
||||
|
||||
|
|
@ -134,6 +136,32 @@ def test_pod_reload_is_due(schedule, pod_data_loaded_at, expected):
|
|||
)
|
||||
|
||||
|
||||
def test_stamps_match_the_precision_they_are_stored_at():
|
||||
"""Postgres stores TIMESTAMP(3), so a microsecond-precision pod clock would read as
|
||||
newer than its own persisted copy and skip the request it just recorded"""
|
||||
assert utc_now().microsecond % 1000 == 0
|
||||
assert to_db_precision(datetime(2024, 1, 1, 6, 0, 0, 123456, tzinfo=timezone.utc)) == datetime(
|
||||
2024, 1, 1, 6, 0, 0, 123000, tzinfo=timezone.utc
|
||||
)
|
||||
|
||||
|
||||
def test_request_in_the_same_millisecond_is_not_lost_to_truncation():
|
||||
"""A pod whose data loaded microseconds before a request must still see it: both sides
|
||||
are floored to the stored resolution, so the request never reads as older than it is"""
|
||||
loaded_at = to_db_precision(datetime(2024, 1, 1, 6, 0, 0, 123400, tzinfo=timezone.utc))
|
||||
requested_at = datetime(2024, 1, 1, 6, 0, 0, 124000, tzinfo=timezone.utc)
|
||||
|
||||
assert (
|
||||
pod_reload_is_due(
|
||||
schedule=ReloadSchedule(reload_requested_at=requested_at),
|
||||
pod_data_loaded_at=loaded_at,
|
||||
current_time=NOW,
|
||||
description="test",
|
||||
)
|
||||
is True
|
||||
)
|
||||
|
||||
|
||||
def test_manual_request_reaches_every_pod_with_older_data():
|
||||
"""Nothing clears reload_requested_at, so each pod reloads exactly once per request:
|
||||
due while its data predates the request, not due once refreshed or booted after it"""
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue