-
-
Notifications
You must be signed in to change notification settings - Fork 60
fix(eap): speed up GetTrace endpoint pagination #7614
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
| ), | ||
| ] | ||
| new_order_by = [ | ||
| OrderBy( | ||
| direction=OrderByDirection.ASC, | ||
| expression=column("timestamp"), | ||
| ), | ||
| OrderBy( | ||
| direction=OrderByDirection.ASC, | ||
| expression=column("item_timestamp"), | ||
| ), | ||
| OrderBy( | ||
| direction=OrderByDirection.ASC, | ||
| expression=column("item_id"), | ||
| ), | ||
| # we add organization_id and project_id to the order by to optimize data reading | ||
| # https://clickhouse.com/docs/sql-reference/statements/select/order-by#optimization-of-data-reading | ||
| OrderBy(direction=OrderByDirection.ASC, expression=column("organization_id")), | ||
| OrderBy(direction=OrderByDirection.ASC, expression=column("project_id")), | ||
| OrderBy(direction=OrderByDirection.ASC, expression=column("item_type")), |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think we still need item_timestamp in the order by (between trace_id and item_id). its a more precise timestamp than timestamp but we want to ensure its ordering. you can double check with pierre, and you will have to verify the implications on performance.
did you already test the performance of the existing order by using set_skip_transform_order_by? If not I think that would be worth testing first before changing the order by.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR fixes a pagination performance issue in the GetTrace endpoint by aligning the ORDER BY clause with the ClickHouse table's sort key. The mismatch was causing slow pagination as ClickHouse couldn't efficiently skip data.
- Updated ORDER BY to match table sort key:
organization_id, project_id, item_type, timestamp, trace_id, item_id - Added
set_skip_transform_order_by(True)to prevent column transformations that would break the alignment - Added test to verify the SQL ORDER BY clause matches the expected ordering
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| snuba/web/rpc/v1/endpoint_get_trace.py | Updated ORDER BY clause to match ClickHouse table sort key (added organization_id, project_id, item_type, trace_id; removed item_timestamp alias). Added set_skip_transform_order_by(True) to preserve exact column names in ORDER BY. |
| tests/web/rpc/v1/test_endpoint_get_trace.py | Added new test test_no_transformation_on_order_by to verify that the ORDER BY clause in generated SQL matches the expected column order without transformations. Added necessary imports (QueryResult, run_query). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| OrderBy(direction=OrderByDirection.ASC, expression=column("organization_id")), | ||
| OrderBy(direction=OrderByDirection.ASC, expression=column("project_id")), | ||
| OrderBy(direction=OrderByDirection.ASC, expression=column("item_type")), | ||
| OrderBy(direction=OrderByDirection.ASC, expression=column("timestamp")), |
Copilot
AI
Jan 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new ORDER BY clause doesn't align with the existing pagination filter logic (page_token_filter at lines 254-283). The page_token_filter uses item_timestamp (a computed alias) and timestamp to determine the next page, but the new ORDER BY doesn't include item_timestamp. This mismatch means pagination will not work correctly.
For correct pagination behavior, either:
- Update the page_token_filter to match this new ORDER BY (filtering on organization_id, project_id, item_type, timestamp, trace_id, item_id)
- Include item_timestamp in the ORDER BY if precision-based pagination is required
- Ensure the page_token stores and filters on all ORDER BY columns in the correct order
Currently, the page_token_filter uses timestamp and item_timestamp, but this ORDER BY only uses timestamp without item_timestamp, which will cause incorrect pagination boundaries.
| OrderBy(direction=OrderByDirection.ASC, expression=column("timestamp")), | |
| OrderBy(direction=OrderByDirection.ASC, expression=column("timestamp")), | |
| OrderBy(direction=OrderByDirection.ASC, expression=column("item_timestamp")), |
| def test_no_transformation_on_order_by(self, setup_teardown: Any, monkeypatch: Any) -> None: | ||
| # Wrap the real run_query to capture the actual QueryResult while still hitting ClickHouse. | ||
| captured: dict[str, Any] = {} | ||
|
|
||
| def wrapper(dataset, request, timer, robust: bool = False, concurrent_queries_gauge=None) -> QueryResult: # type: ignore[no-untyped-def] | ||
| qr = run_query(dataset, request, timer, robust, concurrent_queries_gauge) | ||
| captured["query_result"] = qr | ||
| return qr | ||
|
|
||
| monkeypatch.setattr("snuba.web.rpc.v1.endpoint_get_trace.run_query", wrapper) | ||
|
|
||
| ts = Timestamp(seconds=int(_BASE_TIME.timestamp())) | ||
| three_hours_later = int((_BASE_TIME + timedelta(hours=3)).timestamp()) | ||
| message = GetTraceRequest( | ||
| meta=RequestMeta( | ||
| project_ids=[1, 2, 3], | ||
| organization_id=1, | ||
| cogs_category="something", | ||
| referrer="something", | ||
| start_timestamp=ts, | ||
| end_timestamp=Timestamp(seconds=three_hours_later), | ||
| request_id=_REQUEST_ID, | ||
| ), | ||
| trace_id=_TRACE_ID, | ||
| items=[ | ||
| GetTraceRequest.TraceItem( | ||
| item_type=TraceItemType.TRACE_ITEM_TYPE_SPAN, | ||
| ) | ||
| ], | ||
| ) | ||
|
|
||
| EndpointGetTrace().execute(message) | ||
|
|
||
| qr = captured["query_result"] | ||
| assert ( | ||
| "ORDER BY organization_id ASC, project_id ASC, item_type ASC, timestamp ASC, trace_id ASC, item_id ASC" | ||
| in qr.extra["sql"] | ||
| ) |
Copilot
AI
Jan 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider explicitly setting state.set_config("enable_trace_pagination", 1) at the beginning of this test to make it clear that it's testing the new pagination order by behavior. While pagination is enabled by default (ENABLE_TRACE_PAGINATION_DEFAULT = 1), explicit configuration makes the test intent clearer and more resilient to future default changes.
|
What will you monitor to see if this has an impact on the product in aggregate? |
| column("trace_id"), literal(page_token.last_seen_trace_id) | ||
| ), | ||
| f.greater( | ||
| f.reinterpretAsUInt128(f.reverse(f.unhex(column("item_id")))), | ||
| literal(page_token.last_seen_item_id), | ||
| ), | ||
| ), | ||
| ), | ||
| ), | ||
| ), | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: The keyset pagination filter omits organization_id, creating a mismatch with the ORDER BY clause and negating a performance optimization.
Severity: MEDIUM
Suggested Fix
Update the pagination filter logic to include organization_id as the first condition. The filter conditions should be built to match the full ORDER BY clause: (organization_id, project_id, item_type, timestamp, trace_id, item_id). This will ensure the pagination is logically correct and fully leverages the intended ClickHouse optimization.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: snuba/web/rpc/v1/endpoint_get_trace.py#L327-L394
Potential issue: The keyset pagination filter logic omits `organization_id`, while the
`ORDER BY` clause includes it. A code comment explicitly states `organization_id` is
added to the `ORDER BY` for ClickHouse performance optimization, which relies on the
order matching the table's sort key. Keyset pagination requires the filter conditions to
match the `ORDER BY` columns. Although current queries are filtered to a single
organization, this mismatch renders the optimization ineffective and makes the
pagination logic technically incorrect, risking silent failures if the API is ever used
for cross-organization queries.
Did we get this right? 👍 / 👎 to inform future reviews.
Product team reports pagination to be slow, and we think making the order by clause of the query match the sort key of the CH table will help (consequently we change the pagination to match the order by). This is related to the pre-existing bug uncovered as part of #7586
We will be measuring the speed up by using our internal DD metrics
rollout plan: