Skip to content

api: align pagination cursors with resource ordering - #44

Open
Kerrycek wants to merge 3 commits into
masterfrom
codex/api-ordered-cursors
Open

Kerrycek wants to merge 3 commits into
masterfrom
codex/api-ordered-cursors

Conversation

@Kerrycek

@Kerrycek Kerrycek commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

English

IP assignment history, datasets, snapshots and dataset property history currently sort by dates or names but paginate by ID. With non-monotonic IDs/dates, a next page can omit or repeat rows. VPS user-data also lacks explicit ordering for its ascending ID cursor.

This change preserves the visible order and continues after a scoped (sort value, id) anchor. IP history supports both directions; datasets use full name, snapshots creation time, and property history descending creation time. User-data explicitly uses ascending ID. Dataset/pool joins are deduplicated; history date filters are qualified for joined anchor lookups.

Clients must pass the last returned row ID with the same filters/order. Unknown, deleted, foreign or filtered-out tuple anchors return HTTP 400 Invalid pagination cursor. This deliberately changes error behavior and requires coordinated client handling. It does not guarantee a snapshot across requests or stability when sort values change. No schema migration or shared deployment is included.

Validation: actual Ruby API resource specs against a dedicated MariaDB database; multi-page traversal, tied/non-monotonic values, owner/filter scope, rejected anchors and terminal pages. Final source ee81404: 133 examples, 0 failures (seed 8217); RuboCop: 8 files, no offenses. All modified spec files already belong to existing CI topics; no files were added or moved.

Related frontend work: Kerrycek/clankerdev#496 and Clankerdev issues #241, #208, #189. IP/dataset clients still need coordinated cursor changes and live browser verification before the beta gate can close.

Česky

Historie přiřazení IP, datasety, snapshoty a historie vlastností datasetů řadí podle času nebo názvu, ale stránkují podle ID. Při nesouladu ID a tohoto pořadí další stránka vynechává nebo opakuje záznamy. User-data navíc nemají výslovné pořadí pro vzestupný ID cursor.

Oprava zachovává viditelné pořadí a pokračuje za dvojicí (řadicí hodnota, ID) v autorizovaném a filtrovaném scope. User-data dostávají explicitní vzestupné pořadí ID. Zároveň odstraňuje duplicity z dataset/pool joinu a nejednoznačnost datumových filtrů historie.

Klient musí předávat ID posledního vráceného řádku a zachovat filtry/směr. Nedostupný, smazaný, cizí nebo odfiltrovaný tuple cursor vrací HTTP 400. Jde o záměrnou změnu chybového kontraktu; souběžné změny pořadí vyžadují nový průchod. Změna neobsahuje migraci ani nasazení. Po review je potřeba sladit klienty a ověřit skutečný více-stránkový průchod přes UI/API.

Ověřený commit ee81404: 133 testů bez chyby (seed 8217), RuboCop 8 souborů bez nálezu. Testy používají skutečné Ruby resources a oddělenou MariaDB, nikoli mockované odpovědi. Pokrývají shodné i nemonotónní hodnoty, oprávnění/filtry, chybné cursory a konec seznamu.

Localization validation / Kontrola překladů

Normalized the English and Czech API catalogs for the scoped IP assignment
cursor description while retaining the existing payment description.
bundle exec rake vpsadmin:i18n:update and
bundle exec rake vpsadmin:i18n:health both pass against an isolated test
MariaDB; the test database process was shut down afterwards.

Normalizovány české a anglické popisy cursoru historie IP, se zachováním
původního popisu plateb. Generování katalogů i kontrola úplnosti překladů
prošly nad izolovanou testovací MariaDB; její proces byl následně ukončen.

Engine CI diagnosis / Diagnostika engine CI

The initial full engine job failed two examples at seed 50084. Both exact
failures also reproduce on unchanged API 486350466 without this PR:
ip_release_campaign_spec.rb:720 (verifying double cannot resolve the lazy
deadline attribute) and transaction_chains/export/create_spec.rb:72
(duplicate generated fixture IP). The new-head GitHub full/core engine jobs
have passed without modifying these tests or bypassing any checks.

The isolated baseline run used the existing pinned Ruby dependencies and
MariaDB; it finished 1,184 examples with 31 failures and 3 pending, including
those two. The other failures include concurrency/record-change and mail-template
cases, so this baseline is not presented as a green full suite or an exact CI
environment reproduction. The owned database process was stopped afterward.
This PR remains scoped to cursors; no unrelated engine fixes are included.

Obě původní chyby engine CI se reprodukovaly také na původním API 486350466
bez tohoto PR: lazy atribut deadline v testovacím double a kolize generované
IP adresy. Nové GitHub engine joby prošly bez změny těchto testů. Izolovaný
baseline běh měl celkem 1 184 příkladů, 31 selhání a 3 pending, včetně dalších
concurrency a mail-template chyb; nejde o zelenou úplnou sadu ani totožné CI
prostředí. Testovací databáze je ukončená, nesouvisející opravy nejsou součástí PR.

Continue IP history after the scoped cursor's (from_date, id) tuple in
the requested direction. Reject unavailable cursors instead of returning
a misleading terminal page. Order user-data explicitly by ascending ID.

Cover tied and nonmonotonic dates, owner filters, terminal pages and
invalid cursors with API specs. Keep this proposal local pending approval
to open an upstream PR; no shared API or database changes are included.
Resolve cursor anchors inside authorized filtered queries and continue by
name/time plus ID, preventing omitted and repeated rows across pages.
Deduplicate pool joins and reject invalid anchors consistently. Qualify
history date filters when resolving anchors through included properties.

Cover nonmonotonic values, tied timestamps, scope boundaries, invalid
anchors and terminal pages using actual API resource specs.
Separate IP assignment and payment cursor descriptions after changing
the assignment contract. Regenerate both catalogs and retain the Czech
payment translation so API i18n health checks pass.
@aither64

Copy link
Copy Markdown
Member

Thanks for addressing the mismatch between the visible sort order and ID-based pagination. Keeping the date/name order and using (sort value, id) as the cursor makes sense.

I found one blocker before merge: the existing PHP WebUI sends from_id=0 on the first IP assignment and NAS backup dataset requests. The new IP and dataset cursor code looks for an anchor with ID 0 and returns HTTP 400. The integration job now fails the member and admin networking browser cases with Invalid pagination cursor. Please update those WebUI callers to omit from_id when it is unset or zero, and send it only when it names a real row. Verify the first page and Next link against this API. The existing dataset list already follows that pattern. This WebUI change needs to land with the API change.

I also think these should be addressed before rollout:

  • The PHP IP and NAS filter forms keep from_id when filters change. An anchor excluded by the new filters now produces 400. Please reset the cursor and pagination history on filter changes and provide a first-page recovery path for stale or deleted anchors. This can be a companion WebUI change, but it needs to be coordinated with the API deployment.
  • Dataset, snapshot, and property-history from_id still inherit HaveAPI's description about rows with greater/lower IDs. That no longer describes the (full_name, id) and (created_at, id) cursors. Please update the action descriptions and document the same-filter requirement, 400 response, and client rollout/rollback contract.

For the commit series, I would split the IP assignment and user-data changes in de1cf7b06 into separate commits, fold the dependent locale update 320af0e15 into the IP commit, and keep the dataset-family commit separate. The first commit message also says the work is awaiting approval to open a PR, which is now stale. I would keep these logical changes separate rather than squash the entire PR.

One separate PHP WebUI follow-up: the user-data page renders Next links but never forwards from_id to the API, so page two repeats page one. This predates this PR, but it is worth fixing alongside the client pagination work.

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