Repository navigation
feat(core): support startup modes in StreamTableScan - #1313
Conversation
f28d180 to
e49e479
Compare
|
I suggest removing The table already retains the information needed for this check, so there is no need to cache a separate diagnostic string on |
e49e479 to
93f7f9f
Compare
StreamTableScan could only start from the latest full state: scan.mode latest and from-snapshot-full were rejected, and from-snapshot and from-timestamp were silently ignored. Resolve the starting position from scan.mode, as Java AbstractDataTableScan#createStartingScanner does for streaming, whenever the scan has neither a checkpoint nor consumer progress: - latest: the changes committed after the latest snapshot. - from-snapshot: the changes from snapshot max(scan.snapshot-id, earliest) onward. - from-timestamp: the changes from the first snapshot committed at or after the timestamp onward. - from-snapshot-full: the full state of snapshot max(scan.snapshot-id, earliest), followed by the changes after it. As in Java CoreOptions#startupMode, an unset or default scan.mode selects from-snapshot for scan.snapshot-id, from-timestamp for a timestamp, and latest-full without a selector. The full state emits the merged live rows as +I, while changes keep their physical row kinds. Startup options a stream cannot honor, such as tag, watermark and scan.version selectors or incremental reads, now fail instead of being silently ignored. In the Python binding, new_read_builder(options).new_stream_scan() starts from scan.mode, matching Java's table.copy(options).newReadBuilder().newStreamScan(). Batch scans created by new_scan() and new_incremental_scan() raise when a time-travel selector resolves to no snapshot. Document streaming reads in a new Streaming Reading page.
93f7f9f to
38e6458
Compare
|
@JingsongLi, done in 38e6458. One adjustment: |
Purpose
Linked issue: close #807
StreamTableScancould only start from the latest full state:scan.modelatestandfrom-snapshot-fullwere rejected, whilefrom-snapshotandfrom-timestampwere silently ignored, so a reader asking for the changes since snapshot N got the latest full state instead. This PR resolves the starting position fromscan.modeas JavaAbstractDataTableScan#createStartingScannerdoes in streaming mode.Brief change log
stream_table_scan.rs), used only when the scan has neither a checkpoint nor consumer progress:latest: no initial read; changes after the latest snapshot, or from snapshot 1 on an empty table.from-snapshot: changes frommax(scan.snapshot-id, earliest). The id after the latest snapshot waits for the next commit; a larger id fails the existing range check, as in JavaNextSnapshotFetcher.from-timestamp: changes from the first snapshot committed at or after the timestamp, or from the next commit when every snapshot is older.from-snapshot-full: the full state ofmax(scan.snapshot-id, earliest), then changes. An empty table waits for its first commit; otherwise a missing snapshot fails withSnapshotNotExist.defaultscan.moderesolves as JavaCoreOptions#startupMode:scan.snapshot-idselectsfrom-snapshot, a timestamp selectsfrom-timestamp, and no selector selectslatest-full.+I, without replaying earlier+U/-Devents; later plans keep physical row kinds. Withchangelog-producernoneorinput, the full state merges every level, including uncompacted level-0 files of DV tables;lookupandfull-compactionkeep their level filters.scan.versionselectors, incremental reads, andscan.file-creation-time-millis/scan.creation-time-millis, even next to another selector. Every plan still validates the scan options, so an invalid configuration (e.g.from-snapshot-fullwithoutscan.snapshot-id) also fails after a restore.scan.modeor consumer progress is logged at INFO.SnapshotManager::first_id_at_or_after_time_millis: unlikelater_or_equal_time_millis, it returns the first of several snapshots sharing a commit time, and unlike Java'searlierThanTimeMills + 1, it skips ids missing from the listing instead of waiting on them.table.new_read_builder(options).new_stream_scan()starts fromscan.mode, as Java'stable.copy(options).newReadBuilder().newStreamScan().new_scan()andnew_incremental_scan()check the time-travel selector intable.schema().options()instead ofnew_read_builder(options)raising. A selector the table resolved passes; one it has not resolved, such as one in a resolved schema, is resolved as the plan would and raises if it matches no snapshot.new_stream_scan()skips that check, so a stream can start from the next commit, an expired snapshot, an early timestamp, or an empty table.Tests
snapshot_manager.rs:test_first_id_at_or_after_time_millis_keeps_ties_and_skips_holes.tests/stream_table_scan_test.rs:startup_modes_wait_on_an_empty_tablelatest_and_from_snapshot_start_without_an_initial_state(producersnone/input, ids below the earliest, the next commit, and beyond the latest)from_timestamp_starts_at_the_first_snapshot_at_or_after_it(shared commit times, timestamps before and after every snapshot, a snapshot-id hole)from_snapshot_full_keeps_row_kinds_across_restore(DV and non-DV tables, uncompacted level 0)initial_lookup_and_full_compaction_select_java_levels_only_once(extended with every startup mode forlookup/full-compaction)consumer_progress_overrides_the_startup_modestartup_options_fail_at_startup_and_invalid_ones_also_on_restoreappend_table_streams_from_each_startup_modebindings/python/tests/test_stream_table_scan.py:new_read_builder(options).new_stream_scan()starting at the next commit, an early timestamp, a resolved snapshot, an expired snapshot and an empty table, whilenew_scan()/new_incremental_scan()raise for the unresolved selectors.bindings/python/tests/test_read.py,test_table.py: unresolved selectors raise fromnew_scan().bindings/python/tests/test_resolved_table.py: a resolved-schema selector that matches no snapshot raises fromnew_scan().API and Format
StreamTableScannow honorsscan.modelatest,from-snapshot,from-timestampandfrom-snapshot-full. Tables that setscan.snapshot-idor a timestamp without a checkpoint now start there instead of at the latest full state, and the unsupported startup options above now fail instead of being ignored.Table.new_read_builder(options)no longer raises for a time-travel selector that resolves to no snapshot;new_scan()andnew_incremental_scan()raise instead.Documentation
docs/src/streaming-reading.md: read loop, checkpoints, startup modes, row kinds and limitations; added to themkdocs.ymlnav.docs/src/python-binding.md: continuous reads withnew_read_builder(options).new_stream_scan(), and the batch scans that raise for unresolved selectors.🤖 Generated with Claude Code