Skip to content

perf(gpkg): connection reuse + prepared bulk-insert + keyset paging (MAPCO-11320) - #255

Open
shimoncohen wants to merge 2 commits into
masterfrom
logic-1-gpkgclient-perf
Open

perf(gpkg): connection reuse + prepared bulk-insert + keyset paging (MAPCO-11320)#255
shimoncohen wants to merge 2 commits into
masterfrom
logic-1-gpkgclient-perf

Conversation

@shimoncohen

Copy link
Copy Markdown
Collaborator

LOGIC-1 of MAPCO-11317. Hot-path performance for GpkgClient (shared library used by MergerService — production/OCP — and MergerCli).

Changes

  • Connection reuse: every GpkgClient op opened a fresh SQLiteConnection. Now one lazily-opened, lock-guarded connection per instance, opened with journal_mode=WAL + synchronous=NORMAL. The lock makes it the thread-safe connection SVC-1 (MAPCO-11325) depends on, and also fixes the existing concurrent access from Data.GetLastExistingTile (Parallel.ForEachAsync).
  • Prepared bulk-insert: InsertTiles binds parameters once and reuses a prepared statement per row instead of re-adding parameters each iteration.
  • Keyset paging: GetBatch pages on rowid > cursor ORDER BY rowid (indexed, constant page cost) instead of LIMIT/OFFSET (which rescans discarded rows). Returns (tiles, lastId); Gpkg persists lastId as the batch identifier.
  • Deterministic disposal: GpkgClient : IDisposable closes the connection; IData : IDisposable; both apps dispose sources+target after the merge so file locks/WAL handles are released (matters in the long-running service).

⚠️ Migration note

The gpkg batch identifier changes from an offset to a rowid cursor. Drain in-flight gpkg-source jobs before deploy so resume checkpoints aren't reinterpreted across the format change.

Testing

  • Full suite green: 1141 passed, 0 failed.
  • Keyset paging + cursor advancement, prepared insert, and Dispose idempotency covered against real SQLite in GpkgUtilsTest; mock-based Gpkg paging/reset/resume updated in GpkgTest.

Notes

  • rowid (not a literal id column) is used so paging works on arbitrary source gpkgs regardless of explicit PK naming.
  • Blocks MAPCO-11325 (SVC-1).

🤖 Generated with Claude Code

…PCO-11320)

GpkgClient opened a fresh SQLiteConnection for every tile/batch operation.
Hold one lazily-opened connection per client instead, guarded by a lock so
the client is safe under the concurrent access Data.GetLastExistingTile
(Parallel.ForEachAsync) already performs and that the service-side parallel
merge will add. Open it with WAL + synchronous=NORMAL for concurrent reads
and far fewer fsyncs on the hot path.

- InsertTiles: bind parameters once and reuse a prepared statement per batch
  instead of re-adding parameters on every row.
- GetBatch: keyset pagination on rowid (WHERE rowid > cursor ORDER BY rowid)
  instead of LIMIT/OFFSET, so page cost stays constant as the merge advances.
  Signature returns (tiles, lastId); Gpkg persists lastId as the batch
  identifier for resume.
- GpkgClient is now IDisposable and closes the connection; IData : IDisposable
  and both apps dispose sources+target after the merge to release file locks.

Migration note: the gpkg batch identifier changes from an offset to a rowid
cursor. Drain in-flight gpkg-source jobs before deploying so resume
checkpoints are not reinterpreted across the format change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shimoncohen shimoncohen self-assigned this Aug 5, 2026
@shimoncohen
shimoncohen marked this pull request as ready for review August 5, 2026 08:07
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