-
Notifications
You must be signed in to change notification settings - Fork 614
[PERFORMANCE]: Admin UI endpoints have high tail latency (5-10s p95) #1894
Description
Summary
Under load, Admin UI endpoints show significantly higher latency than API endpoints.
Note: During load testing, /admin/* traffic appeared in Locust stats even when AdminUIUser/RealisticUser were expected to be disabled. Verify Locust class picker settings before attributing baseline latency.
Observed Metrics
| Endpoint | Avg Latency | P95 Latency |
|---|---|---|
/admin/ |
~5283 ms | ~10000 ms |
/admin/tools |
~1800 ms | ~3700 ms |
Root Cause
Multiple contributing factors identified via py-spy profiling:
- N+1 patterns calling
get_member_count()per team (see [PERFORMANCE]: N+1 query pattern in EmailTeam.get_member_count() #1892) - Heavy aggregation queries for dashboard stats
- Pydantic
model_validate()overhead for large result sets - ORM query building overhead
py-spy sample:
Thread 21 (active+gil): "MainThread"
__eq__ (sqlalchemy/sql/operators.py:584)
operate (sqlalchemy/orm/attributes.py:453)
list_tools (mcpgateway/services/tool_service.py:1527)
_call_list_with_team_support (mcpgateway/admin.py:2528)
admin_ui (mcpgateway/admin.py:2649)
Affected Code
mcpgateway/admin.py:3315-3327- loops callingget_member_count()mcpgateway/admin.py:2454, 2528, 2649- admin UI endpointsmcpgateway/services/tool_service.py:1527- list_tools
Proposed Fixes
-
Pagination: Limit items per page, use cursor-based pagination for large lists
-
Lazy loading: Load detailed stats only when requested (HTMX partial loads)
-
Caching: Cache dashboard aggregates in Redis with short TTL
-
Eager loading: Use
joinedload()to prevent N+1 patterns -
Fix N+1 in get_member_count(): See [PERFORMANCE]: N+1 query pattern in EmailTeam.get_member_count() #1892
-
Parallelize service calls: Use
asyncio.gather()for the 7+ sequential service calls atadmin.py:3220-3344(from [PERFORMANCE]: Admin UI endpoint /admin/ has high latency under load #1907 analysis):# Current: 7 sequential calls # Proposed: asyncio.gather() for ~5-10% latency reduction
-
Template size: Admin template is now 695KB / 14,293 lines — consider splitting
Related Issues
- [PERFORMANCE]: Admin UI endpoint /admin/ has high latency under load #1907 — closed as duplicate (focused on
/admin/endpoint specifically; findings merged here) - [PERFORMANCE]: N+1 query pattern in EmailTeam.get_member_count() #1892 — N+1 in
get_member_count()
Acceptance Criteria
-
/admin/p95 latency < 2000ms under load -
/admin/toolsp95 latency < 1000ms under load - Dashboard loads progressively (skeleton + async data)
- Large lists are paginated