Skip to content

[python][ray] read_by_row_id: dynamic_options + return a lazy Dataset#8468

Merged
JingsongLi merged 9 commits into
apache:masterfrom
XiaoHongbo-Hope:read_by_row_id_table_copy
Jul 6, 2026
Merged

[python][ray] read_by_row_id: dynamic_options + return a lazy Dataset#8468
JingsongLi merged 9 commits into
apache:masterfrom
XiaoHongbo-Hope:read_by_row_id_table_copy

Conversation

@XiaoHongbo-Hope

@XiaoHongbo-Hope XiaoHongbo-Hope commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Purpose

Two follow-ups to #8465.

  • dynamic_options: a dict applied via table.copy (as a normal read does), so callers can override read options on a target passed by name — mainly {"blob-as-descriptor": "true"} to return small BlobDescriptor bytes instead of
    materialising large blobs, resolved later with map_with_blobs. Distributed path unchanged.

  • Lazy Dataset: two upfront checks executed the source at call time (so a bucket_join source ran twice). Now the schema is read with fetch_if_missing=False and the empty-source precheck is dropped — limit(1).count() runs only in the empty-target branch (like update_by_row_id), schema preserved by unioning a typed-empty block.

Tests

test_blob_as_descriptor_via_dynamic_options
test_returns_lazy_without_executing_source

… overrides

Apply caller-supplied table options via table.copy before reading -- the
same mechanism a normal read uses -- so e.g. dynamic_options={"blob-as-
descriptor": "true"} returns small BlobDescriptor bytes instead of
materialising large blob payloads in the read task (resolve downstream with
map_with_blobs, bounded batch_size). The option rides on the table object and
flows through the existing scan-snapshot copy, so the distributed read path is
unchanged.
… the source

read_by_row_id returns a Dataset, but two upfront checks forced the source to run
at call time, so a bucket_join source executed twice (once here, once in the
caller's action):

- source_ds.schema() infers a lazy source's schema via an internal limit(1), so
  read it with fetch_if_missing=False and only validate the id column when the
  schema is already known; _project_rid re-checks per batch at run time otherwise.
- the empty-source precheck (rid_ds.limit(1).count()) is gone; the count now runs
  only in the degenerate empty-target branch (mirrors update_by_row_id), and the
  empty-source schema is kept by unioning a typed-empty block into the result.

Adds a test asserting the source is not executed until a downstream action.
@XiaoHongbo-Hope XiaoHongbo-Hope changed the title [python][ray] Support table dynamic when rwad_by_row_id [python][ray] read_by_row_id: dynamic_options + return a lazy Dataset Jul 5, 2026
…azy-read note

- ray-data.md: add dynamic_options to the parameter list; the old 'prefer a
  materialized source' note is stale now the read no longer executes the source
  up front -- replaced with the lazy behaviour.
- trim the dynamic_options docstring to two lines.
…me travel)

Two issues with how dynamic_options was applied:

- It was merged into the table before the capability checks, so a caller could
  override data-evolution.enabled / row-tracking.enabled / deletion-vectors.enabled
  and bypass them (failing later inside the planner with an internal error). The
  checks now run on the persisted table first, and dynamic_options that flip these
  invariants are rejected.

- A time-travel option (scan.snapshot-id / scan.tag-name / ...) was silently ignored:
  the read resolved the latest snapshot and pinned it, returning the wrong snapshot.
  The read snapshot is now resolved from the option via TimeTravelUtil and used for
  both routing and reading; the key is reduced to a plain snapshot-id so the planner's
  own pin does not read as a conflicting second time-travel option.

Tests: reject invariant overrides; time travel by snapshot-id and by tag.
…ghter test asserts

- ray-data.md: 'current snapshot' -> 'resolved target snapshot (latest, or the one
  selected via dynamic_options)' now that time travel is supported.
- narrow the time-travel foreign-id assertions to match the 'valid range' message.
- add a test that two time-travel keys are rejected (TimeTravelUtil mutual exclusion).
@XiaoHongbo-Hope XiaoHongbo-Hope marked this pull request as ready for review July 5, 2026 16:34
…ow-tracking time travel

- table.copy's _try_time_travel swallows TimeTravelUtil's 'more than one time-travel
  option' error (except Exception: return None), so the multi-key rejection was only
  working implicitly via the later _read_snapshot call. Reject >1 scan key explicitly at
  the entry so a refactor can't silently drop it.
- time travel to a snapshot from before row-tracking was enabled has files without row
  ids and crashed deep in the planner (AttributeError). Check the resolved snapshot's own
  schema and raise a clear error instead. (Capability checks stay on the persisted table.)
@JingsongLi

Copy link
Copy Markdown
Contributor

+1

@JingsongLi JingsongLi merged commit 08fbfd9 into apache:master Jul 6, 2026
7 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