Repository navigation
feat: Send state as checkpoints after the resources it covers - #415
Draft
jpollard-cs wants to merge 3 commits into
Draft
jpollard-cs wants to merge 3 commits into
jpollard-cs wants to merge 3 commits into
Conversation
When the CLI sets Sync.Request.supports_checkpoints, a state client built with the scheduler's checkpoints sends flushed state as Sync.MessageCheckpoint instead of writing it to the backend. The CLI then persists it only after every destination commits the records synced before it, so a failed sync keeps the progress it made without moving a cursor past records that were never written. Each checkpoint goes on the sync stream after the resources the table resolvers that changed its keys produced before the flush, including the relations spawned from them, and after earlier unsent checkpoints of the same keys. Also makes the state client thread-safe, clears changed keys after a successful flush and keeps them after a failed one.
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.
Summary
Brings the source side of state checkpoints (cloudquery/plugin-pb#42, cloudquery/plugin-sdk#2632, cloudquery/cloudquery#23546) to the Python SDK. When the CLI sets
Sync.Request.supports_checkpoints, flushed state goes on the sync stream asSync.MessageCheckpointinstead of being written straight to the backend. The CLI then persists it only after every destination commits the records synced before it.Why we want this: some of our Python syncs run for many hours, often limited by provider rate limits. A transient failure late in the sync currently throws away all the progress, because state is only safe to write once the sync ends. With checkpoints, a failed sync keeps the cursors whose records were committed, and the next run resumes from there.
Opt-in, and unchanged unless both sides opt in:
Without
checkpoints, or when the CLI doesn't support them,flush()writes to the backend as it does today.Ordering
This follows the Go SDK's semantics:
flush(), each group of keys becomes a checkpoint. It goes on the scheduler's queue once every resource those scopes produced before the flush is on it, including the relations those resources spawned.versioncolumn, so the CLI relies on stream order.Scheduler.syncreturns.Granularity
Two things set how much a checkpointing sync writes, and today plugin authors and users control only one of them:
state_key(table, *, client_id=None, **partition), so plugins choose the granularity without each inventing a key format. Keys are deterministic, partition fields are sorted, and separators are escaped, so tables, clients and partitions never share a key. Each key is one state row, and a partition's checkpoint waits only for the resolver that set it. Finer keys mean a resumed sync re-fetches less, at the cost of more rows per commit.checkpointIntervalincli/cmd/sync_v3_checkpoints.go). Each commit flushes every destination, then writes every key changed since the last commit to the state backend. The interval is hard-coded.We'd like the commit interval to be configurable per sync, for example in the source spec next to
backend_options, including an option to commit only at the end. The right value depends on the destination and the sync, not on the plugin:Keeping the plugin-side flush cheap matters too, and it is:
flush()in checkpoint mode only puts a message on the sync stream, and nothing touches a database until the CLI commits.Other changes
BackendOptionsgainssupports_checkpoints. The server now buildsBackendOptionsfrom the request instead of passing the protobuf message through.state_keyis exported fromcloudquery.sdk.stateclient.Depends on
The
plugin-pb-pythonbindings with the new messages (cloudquery/plugin-pb-python#250), which aren't released yet (release PR cloudquery/plugin-pb-python#225).pyproject.tomlrequires>=0.0.55, butuv.lockstill needs updating after that release, so CI will fail until then. That's why this is a draft.Open questions
updated_since, that can skip the failed parent's children for good. Would you take an opt-in to hold back a scope's checkpoints when anything in its subtree errored?versioncolumn. Should the Python state table get theversioncolumn, to match the Go client?Write.MessageFlushyet. I left that for a separate PR, since the CLI only enables checkpoints when every destination supports flushing.delete-stale, is a bigger question. I'd like to raise it in a separate issue.Testing
tests/scheduler/checkpoints.py,tests/serve/checkpoints.pyandtests/stateclient/keys.py. They cover:state_keyordering, escaping and uniquenessplugin-pb-pythonmain, excepttest_plugin_package, which builds a Docker image and fails the same way locally onmain.