Repository navigation
Add opt-in cursor pagination for Maven package lists - #543
Merged
Merged
Conversation
kdelee
force-pushed
the
spike-pagination-cursor
branch
from
October 17, 2026 01:29
0bd76b3 to
d51866c
Compare
kdelee
marked this pull request as ready for review
October 19, 2026 16:34
dkliban
approved these changes
Oct 20, 2026
Keep limit/offset pagination as the default while allowing cursor pagination for Maven package lists. Use fixed primary-key-backed ordering and reject client ordering in cursor mode. Assisted-by: pi coding agent
dkliban
force-pushed
the
spike-pagination-cursor
branch
from
October 20, 2026 09:44
d51866c to
1f7b5eb
Compare
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.
Related issue
Part of the LWLP-3265 pagination spike.
Summary
Adds opt-in cursor pagination for Maven repository catalog endpoints. Existing limit/offset pagination remains the default and unchanged.
Supported endpoints:
Enable with:
Cursor mode uses fixed server-side ordering, opaque next/previous links, no
count, and rejects client-providedorderingwith HTTP 400. Existing filters, repository-version selection, RBAC, and bounded page-size handling are preserved.Repository-version cursor pagination is a Maven-only pilot. If validated, it may move to pulpcore so all plugins share one cursor contract.
Traffic analysis
Sanitized ingress analysis covered a 14-day window.
/packages//packages/flat/This is a low-volume control endpoint, but its observed offsets are deeper than
those on
/packages/./versions/GABI query-plan comparison
All plans were sanitized and run with:
Package rows (
/packages/flat/query shape)Using a large completed repository-version fixture with approximately 37,810 MavenPackage rows:
The cursor plan avoided
COUNT(*)andOFFSET, but repository-version membership resolution remained the dominant cost. A membership-first alternative was tested at approximately 3,198 ms and was not adopted.Repository versions (
/versions/)Using the largest completed repository-version fixture found, with approximately 24,646 complete versions:
The offset plan scanned and sorted matching versions before skipping 20,000 rows. The cursor plan used the existing repository-id/version-number composite index.
Grouped package rows (
/packages/)A separate grouped-query GABI run returned approximately 5,707 distinct group/artifact rows from 37,810 package rows:
The grouped cursor plan still grouped all membership rows and removed 4,441 groups after aggregation. Cursor avoids OFFSET, but aggregation and repository-version membership work remain dominant; this is not a constant-time grouped-page claim.
Testing
The functional coverage includes default compatibility, cursor first/next/previous navigation for package lists, repository-version cursor navigation, omission of
count, page-size handling, and rejection of client ordering.Clients were generated with:
Compatibility
This is opt-in only. Existing limit/offset behavior and response contracts remain unchanged. No database migrations are included.
Example usage
First request:
GET .../packages/flat/?pagination=cursor&page_size=100Response includes:
{ "next": ".../packages/flat/?pagination=cursor&page_size=100&cursor=<opaque>", "previous": null, "results": [...] }Follow next verbatim. It includes both: