Repository navigation
fix(api-server): page token transactions by offset instead of cursor - #2136
Open
nullPointerEnjoyer wants to merge 7 commits into
Open
nullPointerEnjoyer wants to merge 7 commits into
nullPointerEnjoyer wants to merge 7 commits into
Conversation
The route handler for GET /api/v2/token/{id}/transactions passes the
request's page offset (skip-N, default 0) but the storage layer treated
the parameter as a cursor (return transactions older than the given
tx_global_index). With the default offset of 0, the cursor predicate
matched nothing and the endpoint returned an empty page for every token
that had no transaction at global index zero.
Align the storage layer with the offset semantics the HTTP API (and every
other paginated endpoint) documents: return the newest transactions
starting at the given offset. An offset past the end of the result set
yields an empty page. Rename the parameter to offset in the trait,
implementations, and wrappers.
Add a storage-level regression test pinning the skip-N semantics of the get_token_transactions offset parameter: the default page starts at the newest transaction, the offset skips entries, and an offset at or past the end of the list yields an empty page instead of an error, including for offsets too large to represent in the database's integer type.
Five postgres queries cast the page offset to a bigint with , which wraps offsets above i64::MAX to a negative number. Postgres then rejects the negative OFFSET and the endpoint fails with an internal error. Guard the conversion the same way the token id queries already do: an offset too big for a bigint is past the end of any result set, so return an empty page.
Truncating the offset to usize with 'as' can wrap an offset that exceeds the pointer width into a small number, silently returning the first page instead of an empty one. Skip by usize::MAX instead, which is always past the end of any in-memory result set. Also deduplicate the bigint offset guard in the postgres queries into a single helper.
nullPointerEnjoyer
requested review from
ImplOfAnImpl,
anyxem and
erubboli
as code owners
October 8, 2026 08:49
|
🔍 OpenCodeReview found 1 issue(s) in this PR.
📄
|
The end-to-end test requested pages with an offset larger than any global index, which only returned data while the storage layer misread the offset as a cursor. Request the pages by their skip counts instead.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
GET /api/v2/token/{id}/transactionsreturned anempty page for every token, because the route passes the request's page offset (skip-N, default 0)
while the storage layer interpreted the parameter as a cursor (
WHERE tx_global_index < $2).offsetentries skipped, empty page at or past the end. Trait parameter renamed to
offsetacross thetrait, both backends, and wrappers.
set, so postgres returns an empty page (five queries previously wrapped
as i64, yielding 500s);the in-memory backend skips by
usize::MAXinstead of truncatingas usize(seven queries),which also fixes first-page-instead-of-empty-page wraps on narrow-pointer targets.
diverged (sort and balance-decode rework); behavior of other endpoints unchanged.
Test plan
token_transactions_offset_paging(both backends): defaultpage starts at the newest transaction, offset skips entries, offsets at/past the end (u64::MAX and
1<<35) yield an empty page instead of an error.
./do_checks.shgreen; OCR review clean.