Skip to content

fix(explore): send a stable sort on errors/discover so cursor pagination is deterministic - #1520

Merged
jared-outpost[bot] merged 3 commits into
mainfrom
issue-1519-explore-stable-sort
Sep 2, 2026
Merged

fix(explore): send a stable sort on errors/discover so cursor pagination is deterministic#1520
jared-outpost[bot] merged 3 commits into
mainfrom
issue-1519-explore-stable-sort

Conversation

@jared-outpost

@jared-outpost jared-outpost Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Multi-page grouped aggregate queries on the errors dataset returned duplicate and missing dimension tuples with a nondeterministic total, while reporting hasMore: false. The events cursor is offset-based, so correct pagination needs a stable total order across the separate page requests — but the CLI dropped the sort param for every non-spans dataset, leaving grouped results in an unstable order that overlapped and skipped rows between pages.

The events endpoint accepts sort on errors/discover (confirmed against a live SaaS org by the reporter: -count() and count() each produce a strictly monotone, repeatable order). This threads the already-computed -<firstAggregate> sort through for those datasets so pagination is deterministic and hasMore is accurate. metrics/logs still reject sort with 400, so they stay unsorted.

Testing

vitest run explore — 58 passing, including new cases asserting errors auto-sorts by the first aggregate and metrics stays unsorted.

Closes #1519

…ion is deterministic

Multi-page grouped aggregate queries on the errors dataset returned
duplicate and missing dimension tuples with a nondeterministic total,
while reporting hasMore: false. The events cursor is offset-based, so
correct pagination requires a stable total order across the separate
page requests — but the CLI dropped the sort param for every non-spans
dataset, leaving grouped results in an unstable order that overlapped
and skipped rows between pages.

The events endpoint accepts sort on errors/discover (confirmed against a
live SaaS org: -count() and count() each produce a strictly monotone,
repeatable order). Thread the already-computed `-<firstAggregate>` sort
through for those datasets so pagination is deterministic and hasMore is
accurate. metrics/logs still reject sort with 400, so they stay unsorted.

Fixes #1519
@vercel

vercel Bot commented Sep 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
cli Ready Ready Preview Sep 2, 2026 3:59am UTC

Request Review

@jared-outpost
jared-outpost Bot marked this pull request as ready for review September 2, 2026 03:55
@github-actions github-actions Bot added the risk: medium PR risk score: medium label Sep 2, 2026
Comment thread packages/cli/src/commands/explore.ts

@BYK BYK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job Jared!

* grouped aggregate queries overlap and skip rows (#1519). `metrics` and `logs`
* reject `sort` with a 400, so they stay unsorted.
*/
const SORTABLE_DATASETS = new Set(["spans", "errors", "discover"]);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I there a way to get this information directly from Sentry codebase, @sentry/api package or Snuba or something?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not in this change — the empirical confirmation from the live-org probe (and the existing spans-only guard) was the minimal, verified fix. A canonical source in @sentry/api or Snuba would be a nice follow-up, but it would require coordination across repos and is out of scope for closing #1519.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you file a follow up for this and merge your PR then?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — opened #1523. Will merge once CI is green on the current commit.

@jared-outpost
jared-outpost Bot merged commit 9941f84 into main Sep 2, 2026
34 checks passed
@jared-outpost
jared-outpost Bot deleted the issue-1519-explore-stable-sort branch September 2, 2026 13:59
BYK pushed a commit that referenced this pull request Sep 2, 2026
…#1524)

Follow-up to #1520. Investigates whether a canonical, machine-readable
source exists for "which datasets accept a sort on grouped aggregates"
and records the finding.

**Result: no such source exists today.** The OpenAPI spec (`@sentry/api`
`listOrganizationEvents`) models `sort` as a flat query param whose only
documented constraint is "must be in the `field` list" — it does not
encode per-dataset sort capability, and there is no dataset-capability
introspection endpoint. The spec's `dataset` enum (`errors | logs |
profile_functions | spans | tracemetrics | uptime_results`) does not
even include `discover`.

Given that, the hand-curated `SORTABLE_DATASETS` set stays, but:
- The `discover` entry is removed — it was unreachable, since `discover`
is not a valid `--dataset` value (see `DATASET_ALIASES`).
- The doc comment now records the research findings as an explicit
contract, with a note to revisit if Sentry ships a per-dataset
capability contract.

## Testing
`vitest run test/commands/explore.test.ts` (40 passed) and
`test/lib/api/explore.test.ts` (18 passed); biome clean on the changed
file.

Refs #1523

<!--
## Plan
1. Investigate candidate canonical sources for sortable-dataset
capability:
- @sentry/api OpenAPI spec (getsentry/sentry-api-schema @ 0.256.0):
fetched openapi-derefed.json.
- listOrganizationEvents `sort` is a flat query param, doc constraint
only "must be in field list".
- dataset enum: errors, logs, profile_functions, spans, tracemetrics,
uptime_results (no discover).
     - no capabilities/dataset-meta endpoint anywhere in spec.
- Conclusion: no machine-readable source; best achievable deliverable is
a documented contract.
2. explore.ts: remove dead `discover` from SORTABLE_DATASETS
(unreachable via DATASET_ALIASES);
   rewrite the doc comment to record the findings and the contract.
3. Verify no test referenced `discover`; run explore unit tests + lint.
-->

Co-authored-by: jared-outpost[bot] <jared-outpost[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: medium PR risk score: medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

explore (errors dataset): duplicate rows and a non-reproducible total while hasMore is false

1 participant