Skip to content

Add chunked payload generation and network transport#50

Open
marcobambini wants to merge 2 commits into
mainfrom
codex/chunked-payloads-network
Open

Add chunked payload generation and network transport#50
marcobambini wants to merge 2 commits into
mainfrom
codex/chunked-payloads-network

Conversation

@marcobambini
Copy link
Copy Markdown
Member

@marcobambini marcobambini commented May 29, 2026

Summary

This PR adds chunk-aware payload generation and send-path transport support to sqlite-sync. It keeps the existing monolithic payload APIs intact while adding a streaming path for large rowsets and oversized individual BLOB/TEXT values.

Implemented changes:

  • Adds cloudsync_payload_chunks():
    • SQLite virtual table interface.
    • PostgreSQL set-returning function interface.
    • Three optional inputs: since_db_version, filter_site_id / local site id, and until_db_version.
    • Emits per-chunk metadata including chunk index, payload size, row count, db version range, and stable watermark.
  • Adds global persisted setting payload_max_chunk_size:
    • Default: 5 MB.
    • Technical minimum: 256 KB, with lower values clamped.
    • Controls only payload generation, not apply-time acceptance.
  • Adds v3 fragment payload support for oversized single BLOB/TEXT values:
    • Large values can be split across multiple transport chunks transparently.
    • Receivers stage fragments internally and apply the final value once all fragments arrive.
    • Duplicate fragment delivery is idempotent and stale incomplete fragment groups are cleaned up.
  • Keeps backward compatibility:
    • Existing cloudsync_payload_encode() remains supported for monolithic payloads.
    • cloudsync_payload_apply() accepts legacy payloads, monolithic payloads, and v3 fragment payloads regardless of the local chunk-size setting.
  • Updates cloudsync_network_send_changes():
    • Streams outgoing changes from cloudsync_payload_chunks() instead of building one large payload first.
    • Sends each chunk through the existing /apply backend contract, either inline as blob or through upload url.
    • Advances the local send checkpoint only after the chunk stream completes successfully.
    • Merges remote status responses monotonically while multiple chunks are in flight.
  • Updates PostgreSQL Docker debug images to build/install dblink, required by the test suite.
  • Updates API, performance docs, README, and changelog.
  • Adds extensive SQLite unit coverage and PostgreSQL SQL coverage for chunk sizing, large-value fragmentation, out-of-order fragments, stale fragment cleanup, and legacy monolithic apply compatibility.

Compatibility

Existing users of cloudsync_payload_encode() and current network APIs continue to work. The new chunking behavior is opt-in for direct SQL callers via cloudsync_payload_chunks(), and automatic for the built-in network send path. Incoming payload apply remains format-compatible with older payloads and does not reject payloads based on the local payload_max_chunk_size.

Companion backend PR

Testing

  • make
  • make unittest
  • make test reached and passed the SQLite/unit portion, then stopped at the remote e2e stage because INTEGRATION_TEST_DATABASE_ID is not set locally.
  • make postgres-docker-debug-rebuild
  • Full PostgreSQL suite via Docker container: psql -U postgres -d postgres -f test/postgresql/full_test.sql (Failures: 0). This was run inside the debug container because the debug PostgreSQL build listens on loopback inside the container and the host make postgres-docker-run-test connection is closed by that setup.
  • git diff --check

marcobambini and others added 2 commits May 29, 2026 11:58
…elpers

- cloudsync_payload_chunks: add exclude_filter_site_id flag (SQLite hidden
  column / PG 4th arg) to stream changes from all sites except filter_site_id,
  as the /check download path needs; setting it without a site_id is an error
- add cloudsync_uuid_text()/cloudsync_uuid_blob() scalar functions on SQLite and
  PostgreSQL to convert site_id between its 16-byte binary form and the canonical
  UUID string (tolerant of dashed/undashed input), so string-based callers can
  pass a site_id to cloudsync_payload_chunks
- sqlite vtab: rewrite best_index to assign argv in canonical column order,
  fixing a latent argument-ordering bug
- perf: throttle the v3 fragment stale-group GC to at most once per 60s per
  connection (cloudsync_context.last_fragment_cleanup), removing an O(n^2)
  full-table scan that ran on every applied fragment
- add PostgreSQL 1.0->1.1 migration for the new chunked-payload SQL surface
- build: neutralize the ambient build env for curl's ./configure (CURL_CONFIG_ENV)
  so exported LDFLAGS/CPPFLAGS/LIBS don't break it
- test: rename PG 39_payload_chunks.sql -> 52 (39 was duplicated); add multi-site
  exclude, UUID roundtrip and stale-GC-throttle coverage (SQLite unit + PG)
- docs: API.md (new argument + two functions) and CHANGELOG

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@andinux
Copy link
Copy Markdown
Collaborator

andinux commented May 30, 2026

Update — commit 92a048c

Adds the /check download-path support to cloudsync_payload_chunks plus related fixes.

Changes

  • exclude_filter_site_id flag on cloudsync_payload_chunks (SQLite hidden column / PG 4th arg): stream all sites except filter_site_id; error if set without a site_id.
  • New cloudsync_uuid_text() / cloudsync_uuid_blob() (SQLite + PG) to convert site_id between 16-byte binary and canonical UUID string, so string callers (the /check endpoint) can pass a site_id.
  • perf: throttled the v3 fragment stale-GC to once/60s per connection — removes an O(n²) full-table scan that ran on every applied fragment.
  • best_index rewritten to assign argv in canonical column order (latent arg-ordering bug fix).
  • PostgreSQL 1.0→1.1 migration for the new SQL surface.
  • build: hermetic env for curl ./configure (CURL_CONFIG_ENV).
  • tests: renamed 39_payload_chunks.sql → 52 (39 was duplicated); added multi-site exclude, UUID roundtrip, and stale-GC-throttle coverage.

Verified: SQLite unittest green; PG full_test.sql → Failures: 0.

TODO

  • Cross-engine v3 fragment round-trip test (SQLite ↔ PG) to lock the payload interop invariant.
  • Integration tests on CI: network send path + receive split-delivery (needs INTEGRATION_TEST_DATABASE_ID).
  • Backend (sqliteai/cloudsync#45): make /check download client-capability-aware — monolithic/v2 for ≤1.0.x clients, chunked/v3 only for new ones; adopt the generate-once download model.
  • Minor cleanups: strtoll(...,10) in dbutils_settings_get_value; contract comment on the memset-from-eof vtab reset; named constant for the fragment-sizing iteration count.

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