Skip to content

fix(DATAGO-134113): Reduce API calls for Context Usage Indicator#1472

Open
amir-ghasemi wants to merge 5 commits into
mainfrom
amir/fix-context
Open

fix(DATAGO-134113): Reduce API calls for Context Usage Indicator#1472
amir-ghasemi wants to merge 5 commits into
mainfrom
amir/fix-context

Conversation

@amir-ghasemi

Copy link
Copy Markdown
Collaborator

This pull request optimizes backend and frontend handling of session context usage, focusing on performance improvements and simplifying code. The most significant changes include making the backend context usage endpoint synchronous for better concurrency, optimizing SQL queries to reduce unnecessary data transfer, and refining frontend polling logic to minimize redundant API calls. Additionally, all related unit tests are updated to match the new synchronous API.

Backend performance and API improvements:

  • Changed get_session_context_usage from async def to regular def, so FastAPI runs it in a threadpool—this prevents blocking the event loop with synchronous database operations and improves concurrency. [1] [2]
  • Optimized database queries in get_session_context_usage to select only the columns actually used, significantly reducing row payload and improving performance for long sessions.
  • Improved the logic for retrieving the first 'request' event per task by using DISTINCT ON at the database layer and projecting only the necessary fields, further reducing data transfer and processing overhead.

Frontend polling and API usage:

  • Updated the frontend's ContextUsageIndicator component to avoid refetching usage data on every message change; now, it only polls after a response is complete, with a capped number of attempts and increased delay to reduce unnecessary API calls and load. [1] [2]

Test updates:

  • Refactored all affected unit tests to use synchronous calls, removing async def and pytest.mark.asyncio decorators to match the updated API. Also updated mocks to support new query chaining. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13]

Frontend was refetching context usage on every messages.length change
(status bubbles, intermediate tool/peer messages flap several times per
turn) and the post-response poller retried up to 4 times — combined,
~5–7 calls per chat turn. Drop the messageCount-driven effect; the
TanStack Query key change handles the initial fetch and the poller
already covers new-task arrival. Cap the poller at 2 attempts (1s, 3s).

Backend now projects only the columns the endpoint reads from
TaskModel (drops initial_request_text and ~10 other unused columns),
adds DISTINCT ON (task_id) to the task-events lookup so fan-out within
a task can never return more than one row, and projects (task_id,
payload) only. ix_task_events_task_created already supports the new
DISTINCT ON path optimally.
…adpool

The body of get_session_context_usage is purely synchronous SQLAlchemy
plus CPU work — no awaits. Declared as async def, it ran on the event
loop and blocked it for the full duration of every DB round-trip
(measured up to ~130 ms on the largest sessions in prod), stalling
every other concurrent request on the worker.

FastAPI auto-runs def handlers in its threadpool, so dropping the
async keyword is sufficient — no asyncio.to_thread wrapping needed and
no AsyncSession migration. Tests updated to call the now-sync handler
directly (drop @pytest.mark.asyncio and the await on each call site).

The same treatment likely applies to most other read-only handlers in
this router; tracking that as separate follow-up since it crosses many
endpoints.
@github-actions

github-actions Bot commented Apr 28, 2026

Copy link
Copy Markdown

⚠️ FOSSA Guard: Licensing (SolaceLabs_solace-agent-mesh) • 1 issue

Compared against main (f221877b7ef0d0f620429f53e8cb7fcb40f6395c) • 1 new, 9 total (9 in base)

ℹ️ Privacy mode enabled - detailed violation information hidden.

  • Other: 1 issue(s)

Scan Report | View Details in FOSSA

@github-actions

github-actions Bot commented Apr 28, 2026

Copy link
Copy Markdown

❌ FOSSA Guard: Vulnerability (SolaceLabs_solace-agent-mesh) • 5 issues (enforcing: critical, high)

Compared against main (f221877b7ef0d0f620429f53e8cb7fcb40f6395c) • 5 new, 8 total (15 in base)

ℹ️ Privacy mode enabled - detailed violation information hidden.

  • High: 1 issue(s) 🚫 BLOCKING
  • Medium: 4 issue(s)

Scan Report | View Details in FOSSA

.distinct(column) compiles to PostgreSQL-only DISTINCT ON and raises
CompileError on SQLite/MySQL. The gateway DB officially supports all
three (dependencies.init_database branches on dialect_name), and
SQLite is the default for the WebUI gateway in configs/gateways/
webui.yaml — so any local-dev or self-hosted user with default config
would have hit a 500 the first time their session had completed tasks.

Unit tests didn't catch it because they use MagicMock and never
compile real SQL; the prod check happened to run on PostgreSQL.

Fix: keep the column projection (still saves payload weight on the
TaskModel side) and the (task_id, created_time ASC) ORDER BY, but
fold to the earliest request per task in Python — same as the
pre-perf-commit code did. The dedup is essentially a no-op anyway
(prod fan-out is ~1 request event per task), so no perf loss.

Tests pin the projection (asserts the TaskModel query selects exactly
the six columns the response builder reads, and that the heavy
initial_request_text is not projected) and add a first-wins-per-task
behavior test so the dedup ordering can't regress silently.
…cator

Three tests pin the behavior changed by the perf patch:
  - poll fires at 1s and 3s after isResponding flips true→false
  - loop exits early once totalTasks >= baseline + 1
  - bumping messageCount alone never triggers a refetch

Two non-obvious setup details worth noting for future test authors:

1. vi.doMock + dynamic import live in a per-test loadComponent helper,
   not in beforeEach. With the number of doMocks here, putting them in
   beforeEach with the dynamic import in the same hook reliably trips
   the 10s hook timeout. Pattern matches RecentChatsList.test.tsx.

2. The lucide-react Proxy mock has to special-case ``then`` and
   well-known Symbols. Returning the Stub component for ``.then`` makes
   the dynamic import runtime mistake the resolved namespace for a
   thenable Promise, await it, and hang forever waiting for resolve to
   be called.
@amir-ghasemi amir-ghasemi requested a review from efunneko April 28, 2026 17:10
@sonarqube-solacecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants