Skip to content

feat(keyset): opt-in Id-range parallel paging for assets batch fetch (v1.4.0) - #111

Merged
aicorey merged 7 commits into
masterfrom
sdk/assets-keyset-paging
Jul 20, 2026
Merged

feat(keyset): opt-in Id-range parallel paging for assets batch fetch (v1.4.0)#111
aicorey merged 7 commits into
masterfrom
sdk/assets-keyset-paging

Conversation

@alphahlee

Copy link
Copy Markdown
Contributor

Summary

Adds an opt-in keyset (Id-range-partitioned) parallel paging mode to the assets batch fetch — ~2–2.7× faster than the current $skip OFFSET paging on large tenants, with no behavior change for existing callers.

getAllAssetsBatch(context, callback) is unchanged; the new mode is reached only via getAllAssetsBatch(context, callback, { paging: 'keyset' }). This is a shared SDK (connector + facility360 mobile + demoio), so the change is purely additive.

How it works

  • getAllBatchKeyset<T extends { Id: number }> probes min(Id)/max(Id), splits the Id space into 16 half-open ranges (Id gt lo and Id le hi), and pages each range with a keyset cursor under the same 4-way Bottleneck pool as getAllBatch (16 partitions are load-balancing granularity, not a concurrency increase — ≤4 requests in flight).
  • Every keyset page forces $orderby=Id; Id is the unique PK, so no row is dropped or duplicated at range boundaries.
  • New QueryContext.buildKeysetUrl / buildKeysetBoundUrl helpers build the page/probe URLs.

Testing

  • Unit: tests/keyset_paging.test.ts — URL builders, row parity across multi-page ranges (dense cluster forces a genuine 2nd page so the cursor-advance loop is exercised), the ≤4 concurrency cap, empty-set, and default-$skip backward-compat. Full suite 110/110 green, tsc clean.

  • Stage (real data, connector-driven, no prod load):

    Tenant Rows (skip / keyset) Parity Speedup
    ccsd copy (~50k) 50,725 / 50,725 1.69× (debug on)
    st-udst stage (~215k) 215,433 / 215,433 2.71× (770s → 284s)

Notes

  • Version bumped 1.3.0 → 1.4.0 (additive minor); CHANGELOG updated.
  • Reference (mega) loaders (userpropertyassociation, etc.) are still $skip — only the assets endpoint is keyset-enabled server-side today; converting those is a separate, larger win to request from Accruent.

🤖 Generated with Claude Code

alphahlee and others added 7 commits July 17, 2026 15:42
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…guard, release safety

- buildKeysetUrl: parenthesize the base filter `(${filter}) and Id gt.. and Id le..`
  so an `or` base filter binds the Id bounds to the whole predicate (OData `and` binds
  tighter than `or`) — prevents silent duplication for future callers. + or-filter test.
- getAllBatchKeyset: tighten range guard with `lo < max` to drop empty ranges above the
  data (avoids ~15 wasted probes on tiny/sparse tenants).
- package.json: add `prepublishOnly: npm run build` + `files: ["dist"]` — dist can't be
  published stale, and the tarball ships only dist (not the 400-line plan doc).
- CHANGELOG + KEYSET_PARTITIONS comment: caveat the speedup as distribution-dependent
  (clustered Ids page mostly sequentially).

Full suite 112/112 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@alphahlee alphahlee self-assigned this Jul 20, 2026
@alphahlee
alphahlee requested review from aicorey and removed request for alexisandreason July 20, 2026 18:37
@aicorey
aicorey merged commit 038cf5b into master Jul 20, 2026
2 checks passed
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