[Fix] should_create_missing_views returns False for reltuples=0 (falsy zero bug)
`should_create_missing_views()` had `and result[0]["reltuples"]` which is falsy when reltuples=0. On a fresh empty PostgreSQL table, CREATE INDEX sets reltuples=0, causing the guard to return False and skip view creation entirely. Views like MonthlyGlobalSpendPerKey are never created, and the /global/spend/logs endpoint returns 500. Fix: change to `and result[0]["reltuples"] is not None` so reltuples=0 (empty table) and reltuples=-1 (unanalyzed table) both correctly return True. Also harden test_vertex_ai.py to return None instead of crashing with JSONDecodeError when the spend-logs endpoint returns a non-JSON 500 response, and add unit tests covering all three reltuples branches (0, -1, positive).
This commit is contained in:
parent
ecf65f5d61
commit
9c0b73e5f4
@ -251,7 +251,7 @@ async def should_create_missing_views(db: _db) -> bool:
|
||||
and len(result) > 0
|
||||
and isinstance(result[0], dict)
|
||||
and "reltuples" in result[0]
|
||||
and result[0]["reltuples"]
|
||||
and result[0]["reltuples"] is not None
|
||||
and (result[0]["reltuples"] == 0 or result[0]["reltuples"] == -1)
|
||||
):
|
||||
verbose_logger.debug("Should create views")
|
||||
|
||||
@ -72,6 +72,10 @@ async def call_spend_logs_endpoint():
|
||||
response = requests.get(url, headers=headers)
|
||||
print("response from call_spend_logs_endpoint", response)
|
||||
|
||||
if response.status_code != 200:
|
||||
print(f"spend logs endpoint returned {response.status_code}: {response.text}")
|
||||
return None
|
||||
|
||||
json_response = response.json()
|
||||
|
||||
# get spend for today
|
||||
|
||||
@ -130,6 +130,42 @@ async def test_create_views_reraises_undefined_function_error():
|
||||
mock_db.execute_raw.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_should_create_missing_views_reltuples_zero():
|
||||
"""should return True when reltuples is 0 (fresh empty table)."""
|
||||
from litellm.proxy.db.create_views import should_create_missing_views
|
||||
|
||||
mock_db = MagicMock()
|
||||
mock_db.query_raw = AsyncMock(return_value=[{"reltuples": 0}])
|
||||
|
||||
result = await should_create_missing_views(mock_db)
|
||||
assert result is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_should_create_missing_views_reltuples_negative_one():
|
||||
"""should return True when reltuples is -1 (table created, no ANALYZE yet)."""
|
||||
from litellm.proxy.db.create_views import should_create_missing_views
|
||||
|
||||
mock_db = MagicMock()
|
||||
mock_db.query_raw = AsyncMock(return_value=[{"reltuples": -1}])
|
||||
|
||||
result = await should_create_missing_views(mock_db)
|
||||
assert result is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_should_create_missing_views_reltuples_positive():
|
||||
"""should return False when reltuples > 0 (table has data)."""
|
||||
from litellm.proxy.db.create_views import should_create_missing_views
|
||||
|
||||
mock_db = MagicMock()
|
||||
mock_db.query_raw = AsyncMock(return_value=[{"reltuples": 1000}])
|
||||
|
||||
result = await should_create_missing_views(mock_db)
|
||||
assert result is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_views_creates_view_on_undefined_table_error():
|
||||
"""should treat 'undefined table' as a missing-view signal and attempt creation."""
|
||||
|
||||
Loading…
Reference in New Issue
Block a user