Skip to content

fix(eap): sort absent order by attributes deterministically when paging - #8246

Draft
JoshuaKGoldberg wants to merge 2 commits into
masterfrom
joshgoldberg/eap-flextime-null-safe-pagination
Draft

fix(eap): sort absent order by attributes deterministically when paging#8246
JoshuaKGoldberg wants to merge 2 commits into
masterfrom
joshgoldberg/eap-flextime-null-safe-pagination

Conversation

@JoshuaKGoldberg

@JoshuaKGoldberg JoshuaKGoldberg commented Jul 31, 2026

Copy link
Copy Markdown
Member

Note

Posting this as a reference & problem statement for review, please forgive what may or may not be idiomatic EAP code. We're happy to close this out if there's a better way.

Tl;dr: we want to add sentry.timestamp.sequence for logs to paginate on. But, unlike sentry.timestamp_precise, it's nullable, and nulls are causing crashes in pages >=2 of paginated queries.


Flextime pagination (FlexibleTimeWindowPageWithFilters, added in #7445) encodes the last row's ORDER BY values in the page token, and the next page asks for the rows less(tuple(order by columns), tuple(bookmark values)). That assumes every ORDER BY column is non-nullable.

Map-backed attributes are not. add_existence_check_to_subscriptable_references selects them as if(mapContains(...), attributes_x[...], NULL), and convert_results turns an absent key into AttributeValue(is_null=True).

So putting any sparse attribute in a flextime ORDER BY breaks in two ways:

  1. get_filters read the bookmark value with getattr(value, str(value.WhichOneof("value"))). is_null is field 11 and is not part of the value oneof, so WhichOneof returns None and the request dies with AttributeError: None
  2. Even reading it, a NULL element makes the whole tuple comparison NULL, so the row is dropped instead of paginated past.

...so you get a 500 on page 2 onward for nullable attributes.

Logs use sentry.timestamp.sequence as the ordering tiebreaker after timestamp and timestamp_precise. SDKs that predate the counter don't emit it, so that attribute is sparse and therefore nullable.

Sentry side: getsentry/sentry#120451 <- LOGS-569.

A flextime page token encodes the last row's ORDER BY values, and the next page
asks for the rows less than that tuple. Both halves of that break when an ORDER
BY column can be NULL: `get_filters` raised `AttributeError: None` reading a null
bookmark value, since `is_null` is not part of the `value` oneof, and a NULL tuple
element makes the comparison NULL, which drops the row rather than paging past it.

Absent map-backed keys now sort as their type's zero value in both the ORDER BY
and the page boundary, so any nullable attribute can take part in a flextime
ORDER BY. Sentry needs that to break log ordering ties on
`sentry.timestamp.sequence`, which older SDKs do not emit.
…ision

`time_window is not None` says the routing strategy chose to page by keyset, but
`get_routing_decision` swallows an estimation failure and rebuilds the decision
with no time window, while the client's page token still compares against the
ORDER BY of the page before it. Ordering absent keys on that signal could put the
two out of step, silently skipping or repeating rows at a page boundary. The
requested flextime mode is stable across a pagination session, so key off that.

Also stop stamping a type on normalized page-token columns: those are never NULL
and their ORDER BY compares the raw column, so the sentinel belongs only on the
map-backed columns the resolver wraps.
@JoshuaKGoldberg JoshuaKGoldberg changed the title fix(eap): Sort absent order by attributes deterministically when paging fix(eap): sort absent order by attributes deterministically when paging Aug 3, 2026

@k-fish k-fish 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.

👍 I can't speak to this being idiomatic Snuba code (get an EAP member to look); but high level we need to do this unfortunately, and we don't want to ingest sequence always so null needs to be handled in paging.

@JoshuaKGoldberg
JoshuaKGoldberg marked this pull request as ready for review August 13, 2026 16:43
@JoshuaKGoldberg
JoshuaKGoldberg requested review from a team as code owners August 13, 2026 16:43
@JoshuaKGoldberg
JoshuaKGoldberg marked this pull request as draft August 13, 2026 16:44
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