[data] Add write_delta release benchmark - #64925
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces support for writing Ray Datasets to Delta Lake tables by implementing a new DeltaDatasink and exposing it via Dataset.write_delta. It includes comprehensive unit tests, benchmark integration, and release test configurations. The review feedback highlights a critical path-resolution bug when writing to cloud storage (such as S3) due to mismatched leading slashes, and recommends updating the type hint for ray_remote_args to be PEP 484 compliant.
d420387 to
3c8305a
Compare
Ports a small, independently-authored prototype write_delta implementation for a side-by-side comparison against the standalone write_delta design on stack2/pr1-write-delta-core. Dataset.write_delta and the DeltaDatasink class are kept as close to the shared original as possible -- deliberate fixes are called out below; everything else is unmodified -- so the diff between the two stacks reflects genuine design differences, not incidental ones. Supports SaveMode.APPEND and SaveMode.OVERWRITE (a prototype scope, by design): workers write Parquet files via pyarrow.dataset.write_dataset and report AddAction metadata; the driver commits all files in one Delta transaction via the deltalake library, creating the table if it doesn't exist. Also supports partition_by (Hive-style), storage_options, and optional name/description table metadata. Bug fix 1 (found while building a write_delta release benchmark on top of this branch): a Dataset sourced from a file-listing-based read (e.g. read_parquet) failed every write with "Schema error: Invalid data type for Delta Lake: Null". Root cause: on_write_start's schema argument can be Ray Data's internal file-listing metadata schema (e.g. a null-typed __file_chunk_metadata column) rather than the dataset's actual row schema, and _resolve_schema unconditionally preferred that captured schema over the real schema workers observed while writing. Fixed by having write() return the schema of the Arrow table(s) it actually wrote, and having _resolve_schema prefer that over the driver-side capture, which now serves only its originally-intended purpose: a fallback for a genuinely empty dataset with no worker-observed schema at all. Addresses review feedback from cursor[bot] and gemini-code-assist[bot] on both PR ray-project#64923 (this branch) and PR ray-project#64925 (the release-benchmark branch stacked on top, whose cumulative diff includes this file), across four review rounds: - Worker tasks now honor storage_options when building their Parquet write filesystem (previously only the driver's DeltaTable/ create_table_with_add_actions calls received it, so a cloud write with explicit credentials could succeed on the driver's commit while every worker's file write silently used different, ambient credentials, or failed outright). - An empty-dataset APPEND against an already-existing table is now a pure no-op -- no schema resolution, no empty commit added to the Delta log. APPEND against a not-yet-existing table with no resolvable schema still raises (a brand-new table can't be created without knowing its shape). - An empty-dataset OVERWRITE against an already-existing table now falls back to that table's own current schema instead of raising: truncating an existing table is well-defined even when the incoming write has no rows (and therefore no worker-observed schema) to resolve one from. - Partition value/key parsing: a NULL partition value (written by PyArrow's Hive writer as the literal directory name "__HIVE_DEFAULT_PARTITION__") now round-trips back as Python None instead of that literal string; partition column names are now URL-unquoted the same way values already were. - write() now unifies the schema across every non-empty block a single task writes, instead of keeping only the last block's schema (a task can receive multiple blocks with promotable but non-identical schemas). - The worker-side Azure filesystem helper now reads the SAS token from AZURE_STORAGE_SAS_TOKEN, matching ray.data.catalog's vended-credential key, instead of a different name that would never match. - The worker-side GCS filesystem helper no longer leaves GOOGLE_APPLICATION_CREDENTIALS mutated after use -- the env var is a process-wide value that could otherwise leak a task's credential path to other tasks reusing the same warm Ray worker. - The written-file-to-table-root relative path computation now strips leading slashes from both sides before computing the relative path: some cloud filesystems can return a table root without a leading slash from FileSystem.from_uri while written-file paths from that same filesystem still carry one (verified against a real S3-compatible server), and that mismatch previously made the relative path walk upward instead of resolving correctly, corrupting the Delta log. - The worker-side S3 filesystem helper now also accepts AWS_DEFAULT_REGION (a common boto-style key) as a fallback when AWS_REGION isn't set, matching what the driver's commit path (which forwards the full storage_options dict to deltalake) can already honor. - Dataset.write_delta's ray_remote_args parameter is now typed Optional[Dict[str, Any]], matching its actual default of None. Adds a new test suite (test_write_delta.py) covering what this implementation actually supports: APPEND/OVERWRITE round-trips, unsupported-mode rejection, single- and multi-column partitioning, multi-block writes (including cross-block schema unification within a single task), table name/description metadata, the empty-dataset error path (and the empty-APPEND-against-existing-table and empty-OVERWRITE-against-existing-table no-op/truncate paths), storage_options passthrough to both the commit calls and the worker filesystem (S3, Azure, and GCS, including the GCS env-var restore behavior and the AWS_DEFAULT_REGION fallback), null/URL-encoded partition value and key handling, a simulated cross-filesystem leading-slash mismatch in the written-file relative path computation, and a regression test for the read_parquet-sourced schema bug above. One pyproject.toml per-file-ignore added (F401) to keep the ported file lint-clean without editing it for that. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Abhishek Verma <abhishek.verma@anyscale.com>
3c8305a to
6ef6d60
Compare
6ef6d60 to
de0eaf8
Compare
Ports a small, independently-authored prototype write_delta implementation for a side-by-side comparison against the standalone write_delta design on stack2/pr1-write-delta-core. Dataset.write_delta and the DeltaDatasink class are kept as close to the shared original as possible -- deliberate fixes are called out below; everything else is unmodified -- so the diff between the two stacks reflects genuine design differences, not incidental ones. Supports SaveMode.APPEND and SaveMode.OVERWRITE (a prototype scope, by design): workers write Parquet files via pyarrow.dataset.write_dataset and report AddAction metadata; the driver commits all files in one Delta transaction via the deltalake library, creating the table if it doesn't exist. Also supports partition_by (Hive-style), storage_options, and optional name/description table metadata. Bug fix 1 (found while building a write_delta release benchmark on top of this branch): a Dataset sourced from a file-listing-based read (e.g. read_parquet) failed every write with "Schema error: Invalid data type for Delta Lake: Null". Root cause: on_write_start's schema argument can be Ray Data's internal file-listing metadata schema (e.g. a null-typed __file_chunk_metadata column) rather than the dataset's actual row schema, and _resolve_schema unconditionally preferred that captured schema over the real schema workers observed while writing. Fixed by having write() return the schema of the Arrow table(s) it actually wrote, and having _resolve_schema prefer that over the driver-side capture, which now serves only its originally-intended purpose: a fallback for a genuinely empty dataset with no worker-observed schema at all. Bug fix 2 (found from an actual CI run of the release benchmark, build 101454): a large distributed write (TPCH SF1000, ~1000 concurrent write tasks against a brand-new table root) failed with "AWS Error SLOW_DOWN during PutObject operation" while writing Parquet files, before any commit was attempted. Root cause: pyarrow.dataset.write_dataset defaults to create_dir=True, which for an S3-backed filesystem issues a PutObject to a single marker key representing the table root directory (verified empirically -- S3 has no real directories, so this is purely a convention). With ~1000 tasks all racing to create that identical key against a cold prefix, the concurrent-PUT burst triggers AWS throttling for no functional benefit (object writes don't need a pre-existing directory marker). Fixed by passing create_dir=False specifically when the resolved filesystem is a cloud object store (S3FileSystem, GcsFileSystem, AzureFileSystem); local (and other real) filesystems still get create_dir=True since they genuinely need the directory to exist (verified separately that create_dir=False raises FileNotFoundError against a local path that doesn't exist yet). Addresses review feedback from cursor[bot] and gemini-code-assist[bot] on both PR ray-project#64923 (this branch) and PR ray-project#64925 (the release-benchmark branch stacked on top, whose cumulative diff includes this file), across four review rounds: - Worker tasks now honor storage_options when building their Parquet write filesystem (previously only the driver's DeltaTable/ create_table_with_add_actions calls received it, so a cloud write with explicit credentials could succeed on the driver's commit while every worker's file write silently used different, ambient credentials, or failed outright). - An empty-dataset APPEND against an already-existing table is now a pure no-op -- no schema resolution, no empty commit added to the Delta log. APPEND against a not-yet-existing table with no resolvable schema still raises (a brand-new table can't be created without knowing its shape). - An empty-dataset OVERWRITE against an already-existing table now falls back to that table's own current schema instead of raising: truncating an existing table is well-defined even when the incoming write has no rows (and therefore no worker-observed schema) to resolve one from. - Partition value/key parsing: a NULL partition value (written by PyArrow's Hive writer as the literal directory name "__HIVE_DEFAULT_PARTITION__") now round-trips back as Python None instead of that literal string; partition column names are now URL-unquoted the same way values already were. - write() now unifies the schema across every non-empty block a single task writes, instead of keeping only the last block's schema (a task can receive multiple blocks with promotable but non-identical schemas). - The worker-side Azure filesystem helper now reads the SAS token from AZURE_STORAGE_SAS_TOKEN, matching ray.data.catalog's vended-credential key, instead of a different name that would never match. - The worker-side GCS filesystem helper no longer leaves GOOGLE_APPLICATION_CREDENTIALS mutated after use -- the env var is a process-wide value that could otherwise leak a task's credential path to other tasks reusing the same warm Ray worker. - The written-file-to-table-root relative path computation now strips leading slashes from both sides before computing the relative path: some cloud filesystems can return a table root without a leading slash from FileSystem.from_uri while written-file paths from that same filesystem still carry one (verified against a real S3-compatible server), and that mismatch previously made the relative path walk upward instead of resolving correctly, corrupting the Delta log. - The worker-side S3 filesystem helper now also accepts AWS_DEFAULT_REGION (a common boto-style key) as a fallback when AWS_REGION isn't set, matching what the driver's commit path (which forwards the full storage_options dict to deltalake) can already honor. - Dataset.write_delta's ray_remote_args parameter is now typed Optional[Dict[str, Any]], matching its actual default of None. Adds a new test suite (test_write_delta.py) covering what this implementation actually supports: APPEND/OVERWRITE round-trips, unsupported-mode rejection, single- and multi-column partitioning, multi-block writes (including cross-block schema unification within a single task), table name/description metadata, the empty-dataset error path (and the empty-APPEND-against-existing-table and empty-OVERWRITE-against-existing-table no-op/truncate paths), storage_options passthrough to both the commit calls and the worker filesystem (S3, Azure, and GCS, including the GCS env-var restore behavior and the AWS_DEFAULT_REGION fallback), null/URL-encoded partition value and key handling, a simulated cross-filesystem leading-slash mismatch in the written-file relative path computation, a regression test for the read_parquet-sourced schema bug above, and a regression test that create_dir is only skipped for cloud object-store filesystems. One pyproject.toml per-file-ignore added (F401) to keep the ported file lint-clean without editing it for that. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Abhishek Verma <abhishek.verma@anyscale.com>
de0eaf8 to
152518f
Compare
4016849 to
e74c587
Compare
| filesystem=filesystem, | ||
| format="parquet", | ||
| partitioning=partitioning, | ||
| basename_template=f"part-{task_idx:05d}-{write_uuid}-{{i}}.parquet", |
There was a problem hiding this comment.
Ignores job-level write uuid
Medium Severity
Each _write_parquet call generates a new uuid.uuid4() for the basename instead of using the job-level write_uuid Ray Data injects into TaskContext.kwargs. Retried write tasks therefore land under new object keys, leaving orphaned Parquet files on storage and preventing existing_data_behavior="overwrite_or_ignore" from reusing the same paths on retry.
Reviewed by Cursor Bugbot for commit e74c587. Configure here.
d201482 to
ca5ed49
Compare
3a0fa63 to
15da910
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
Reviewed by Cursor Bugbot for commit 15da910. Configure here.
12b1451 to
0bf3b90
Compare
7c4fb1c to
986ee6c
Compare
Ports a small, independently-authored prototype write_delta implementation for a side-by-side comparison against the standalone write_delta design on stack2/pr1-write-delta-core. Dataset.write_delta and the DeltaDatasink class are kept as close to the shared original as possible -- deliberate fixes are called out below; everything else is unmodified -- so the diff between the two stacks reflects genuine design differences, not incidental ones. Supports SaveMode.APPEND and SaveMode.OVERWRITE (a prototype scope, by design): workers write Parquet files via pyarrow.dataset.write_dataset and report AddAction metadata; the driver commits all files in one Delta transaction via the deltalake library, creating the table if it doesn't exist. Also supports partition_by (Hive-style), storage_options, and optional name/description table metadata. Bug fix 1 (found while building a write_delta release benchmark on top of this branch): a Dataset sourced from a file-listing-based read (e.g. read_parquet) failed every write with "Schema error: Invalid data type for Delta Lake: Null". Root cause: on_write_start's schema argument can be Ray Data's internal file-listing metadata schema (e.g. a null-typed __file_chunk_metadata column) rather than the dataset's actual row schema, and _resolve_schema unconditionally preferred that captured schema over the real schema workers observed while writing. Fixed by having write() return the schema of the Arrow table(s) it actually wrote, and having _resolve_schema prefer that over the driver-side capture, which now serves only its originally-intended purpose: a fallback for a genuinely empty dataset with no worker-observed schema at all. Bug fix 2 (found from an actual CI run of the release benchmark, build 101454): a large distributed write (TPCH SF1000, ~1000 concurrent write tasks against a brand-new table root) failed with "AWS Error SLOW_DOWN during PutObject operation" while writing Parquet files, before any commit was attempted. Root cause: pyarrow.dataset.write_dataset defaults to create_dir=True, which for an S3-backed filesystem issues a PutObject to a single marker key representing the table root directory (verified empirically -- S3 has no real directories, so this is purely a convention). With ~1000 tasks all racing to create that identical key against a cold prefix, the concurrent-PUT burst triggers AWS throttling for no functional benefit (object writes don't need a pre-existing directory marker). Fixed by passing create_dir=False specifically when the resolved filesystem is a cloud object store (S3FileSystem, GcsFileSystem, AzureFileSystem); local (and other real) filesystems still get create_dir=True since they genuinely need the directory to exist (verified separately that create_dir=False raises FileNotFoundError against a local path that doesn't exist yet). Addresses review feedback from cursor[bot] and gemini-code-assist[bot] on both PR ray-project#64923 (this branch) and PR ray-project#64925 (the release-benchmark branch stacked on top, whose cumulative diff includes this file), across four review rounds: - Worker tasks now honor storage_options when building their Parquet write filesystem (previously only the driver's DeltaTable/ create_table_with_add_actions calls received it, so a cloud write with explicit credentials could succeed on the driver's commit while every worker's file write silently used different, ambient credentials, or failed outright). - An empty-dataset APPEND against an already-existing table is now a pure no-op -- no schema resolution, no empty commit added to the Delta log. APPEND against a not-yet-existing table with no resolvable schema still raises (a brand-new table can't be created without knowing its shape). - An empty-dataset OVERWRITE against an already-existing table now falls back to that table's own current schema instead of raising: truncating an existing table is well-defined even when the incoming write has no rows (and therefore no worker-observed schema) to resolve one from. - Partition value/key parsing: a NULL partition value (written by PyArrow's Hive writer as the literal directory name "__HIVE_DEFAULT_PARTITION__") now round-trips back as Python None instead of that literal string; partition column names are now URL-unquoted the same way values already were. - write() now unifies the schema across every non-empty block a single task writes, instead of keeping only the last block's schema (a task can receive multiple blocks with promotable but non-identical schemas). - The worker-side Azure filesystem helper now reads the SAS token from AZURE_STORAGE_SAS_TOKEN, matching ray.data.catalog's vended-credential key, instead of a different name that would never match. - The worker-side GCS filesystem helper no longer leaves GOOGLE_APPLICATION_CREDENTIALS mutated after use -- the env var is a process-wide value that could otherwise leak a task's credential path to other tasks reusing the same warm Ray worker. - The written-file-to-table-root relative path computation now strips leading slashes from both sides before computing the relative path: some cloud filesystems can return a table root without a leading slash from FileSystem.from_uri while written-file paths from that same filesystem still carry one (verified against a real S3-compatible server), and that mismatch previously made the relative path walk upward instead of resolving correctly, corrupting the Delta log. - The worker-side S3 filesystem helper now also accepts AWS_DEFAULT_REGION (a common boto-style key) as a fallback when AWS_REGION isn't set, matching what the driver's commit path (which forwards the full storage_options dict to deltalake) can already honor. - Dataset.write_delta's ray_remote_args parameter is now typed Optional[Dict[str, Any]], matching its actual default of None. Adds a new test suite (test_write_delta.py) covering what this implementation actually supports: APPEND/OVERWRITE round-trips, unsupported-mode rejection, single- and multi-column partitioning, multi-block writes (including cross-block schema unification within a single task), table name/description metadata, the empty-dataset error path (and the empty-APPEND-against-existing-table and empty-OVERWRITE-against-existing-table no-op/truncate paths), storage_options passthrough to both the commit calls and the worker filesystem (S3, Azure, and GCS, including the GCS env-var restore behavior and the AWS_DEFAULT_REGION fallback), null/URL-encoded partition value and key handling, a simulated cross-filesystem leading-slash mismatch in the written-file relative path computation, a regression test for the read_parquet-sourced schema bug above, and a regression test that create_dir is only skipped for cloud object-store filesystems. One pyproject.toml per-file-ignore added (F401) to keep the ported file lint-clean without editing it for that. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Abhishek Verma <abhishek.verma@anyscale.com>
986ee6c to
3506500
Compare
Ports a small, independently-authored prototype write_delta implementation for a side-by-side comparison against the standalone write_delta design on stack2/pr1-write-delta-core. Dataset.write_delta and the DeltaDatasink class are kept as close to the shared original as possible -- deliberate fixes are called out below; everything else is unmodified -- so the diff between the two stacks reflects genuine design differences, not incidental ones. Supports SaveMode.APPEND and SaveMode.OVERWRITE (a prototype scope, by design): workers write Parquet files via pyarrow.dataset.write_dataset and report AddAction metadata; the driver commits all files in one Delta transaction via the deltalake library, creating the table if it doesn't exist. Also supports partition_by (Hive-style), storage_options, and optional name/description table metadata. Bug fix 1 (found while building a write_delta release benchmark on top of this branch): a Dataset sourced from a file-listing-based read (e.g. read_parquet) failed every write with "Schema error: Invalid data type for Delta Lake: Null". Root cause: on_write_start's schema argument can be Ray Data's internal file-listing metadata schema (e.g. a null-typed __file_chunk_metadata column) rather than the dataset's actual row schema, and _resolve_schema unconditionally preferred that captured schema over the real schema workers observed while writing. Fixed by having write() return the schema of the Arrow table(s) it actually wrote, and having _resolve_schema prefer that over the driver-side capture, which now serves only its originally-intended purpose: a fallback for a genuinely empty dataset with no worker-observed schema at all. Bug fix 2 (found from an actual CI run of the release benchmark, build 101454): a large distributed write (TPCH SF1000, ~1000 concurrent write tasks against a brand-new table root) failed with "AWS Error SLOW_DOWN during PutObject operation" while writing Parquet files, before any commit was attempted. Root cause: pyarrow.dataset.write_dataset defaults to create_dir=True, which for an S3-backed filesystem issues a PutObject to a single marker key representing the table root directory (verified empirically -- S3 has no real directories, so this is purely a convention). With ~1000 tasks all racing to create that identical key against a cold prefix, the concurrent-PUT burst triggers AWS throttling for no functional benefit (object writes don't need a pre-existing directory marker). Fixed by passing create_dir=False specifically when the resolved filesystem is a cloud object store (S3FileSystem, GcsFileSystem, AzureFileSystem); local (and other real) filesystems still get create_dir=True since they genuinely need the directory to exist (verified separately that create_dir=False raises FileNotFoundError against a local path that doesn't exist yet). Addresses review feedback from cursor[bot] and gemini-code-assist[bot] on both PR ray-project#64923 (this branch) and PR ray-project#64925 (the release-benchmark branch stacked on top, whose cumulative diff includes this file), across four review rounds: - Worker tasks now honor storage_options when building their Parquet write filesystem (previously only the driver's DeltaTable/ create_table_with_add_actions calls received it, so a cloud write with explicit credentials could succeed on the driver's commit while every worker's file write silently used different, ambient credentials, or failed outright). - An empty-dataset APPEND against an already-existing table is now a pure no-op -- no schema resolution, no empty commit added to the Delta log. APPEND against a not-yet-existing table with no resolvable schema still raises (a brand-new table can't be created without knowing its shape). - An empty-dataset OVERWRITE against an already-existing table now falls back to that table's own current schema instead of raising: truncating an existing table is well-defined even when the incoming write has no rows (and therefore no worker-observed schema) to resolve one from. - Partition value/key parsing: a NULL partition value (written by PyArrow's Hive writer as the literal directory name "__HIVE_DEFAULT_PARTITION__") now round-trips back as Python None instead of that literal string; partition column names are now URL-unquoted the same way values already were. - write() now unifies the schema across every non-empty block a single task writes, instead of keeping only the last block's schema (a task can receive multiple blocks with promotable but non-identical schemas). - The worker-side Azure filesystem helper now reads the SAS token from AZURE_STORAGE_SAS_TOKEN, matching ray.data.catalog's vended-credential key, instead of a different name that would never match. - The worker-side GCS filesystem helper no longer leaves GOOGLE_APPLICATION_CREDENTIALS mutated after use -- the env var is a process-wide value that could otherwise leak a task's credential path to other tasks reusing the same warm Ray worker. - The written-file-to-table-root relative path computation now strips leading slashes from both sides before computing the relative path: some cloud filesystems can return a table root without a leading slash from FileSystem.from_uri while written-file paths from that same filesystem still carry one (verified against a real S3-compatible server), and that mismatch previously made the relative path walk upward instead of resolving correctly, corrupting the Delta log. - The worker-side S3 filesystem helper now also accepts AWS_DEFAULT_REGION (a common boto-style key) as a fallback when AWS_REGION isn't set, matching what the driver's commit path (which forwards the full storage_options dict to deltalake) can already honor. - Dataset.write_delta's ray_remote_args parameter is now typed Optional[Dict[str, Any]], matching its actual default of None. Adds a new test suite (test_write_delta.py) covering what this implementation actually supports: APPEND/OVERWRITE round-trips, unsupported-mode rejection, single- and multi-column partitioning, multi-block writes (including cross-block schema unification within a single task), table name/description metadata, the empty-dataset error path (and the empty-APPEND-against-existing-table and empty-OVERWRITE-against-existing-table no-op/truncate paths), storage_options passthrough to both the commit calls and the worker filesystem (S3, Azure, and GCS, including the GCS env-var restore behavior and the AWS_DEFAULT_REGION fallback), null/URL-encoded partition value and key handling, a simulated cross-filesystem leading-slash mismatch in the written-file relative path computation, a regression test for the read_parquet-sourced schema bug above, and a regression test that create_dir is only skipped for cloud object-store filesystems. One pyproject.toml per-file-ignore added (F401) to keep the ported file lint-clean without editing it for that. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Abhishek Verma <abhishek.verma@anyscale.com>
Adds a write_delta counterpart to the existing write_parquet release
benchmark, reusing the same real dataset and harness rather than
inventing new synthetic-data infrastructure:
- read_and_consume_benchmark.py: add a --write-delta consume option,
mirroring the existing --write (write_parquet) option exactly --
same read path, same Benchmark/BenchmarkMetric reporting, just a
different write call.
- release_data_tests.yaml: register a write_delta test entry alongside
write_parquet, reading the same TPCH SF1000 lineitem source
(s3://ray-benchmark-data/tpch/parquet/sf1000/lineitem), so the two
are a direct, apples-to-apples Parquet-vs-Delta write comparison on
identical input data.
- byod_install_deltalake.sh: new post-build script installing
deltalake==1.5.0 (matching the version pinned in
python/requirements_compiled*.txt), needed because -- unlike
parquet/image/tfrecords -- deltalake isn't in the base release-test
image. Mirrors byod_install_pyiceberg.sh's exact shape.
Bug fix (found from an actual CI run of this test, build 101449): the
write itself succeeded (6B rows, 815GiB written), but the subsequent
commit step failed with "OSError: Generic S3 error ... Received
redirect without LOCATION, this normally indicates an incorrectly
configured region". Root cause: the ray-data-write-benchmark bucket is
in us-west-2 (confirmed via its S3 HEAD response's x-amz-bucket-region
header), but deltalake's Rust S3 client defaults to us-east-1 and,
unlike PyArrow's S3FileSystem (which write_parquet uses and which
resolves a bucket's region automatically), doesn't follow a region
redirect -- it needs the region supplied explicitly via
storage_options. Fixed by passing storage_options={"AWS_REGION":
"us-west-2"} to the write_delta call, matching the us-west-2 region
convention already used elsewhere in this same directory
(training_ingest_benchmark.py's S3_IMAGE_AWS_REGION,
multi_node_train_benchmark.py's AWS_REGION env var).
Second bug fix (found from a subsequent CI run, build 101454): a large
distributed write (~1000 concurrent tasks against a brand-new table
root) failed with "AWS Error SLOW_DOWN during PutObject operation"
while writing Parquet files. Root cause (fixed on stack3/pr1-write-delta-core,
which this branch is stacked on): pyarrow.dataset.write_dataset's default
create_dir=True made every task independently PUT the same directory
marker key for the table root, and ~1000 tasks racing on that one key
triggered AWS throttling. Fixed there by skipping create_dir for cloud
object-store filesystems (S3/GCS/Azure), which have no real
directories.
Adds three more write_delta benchmark variants on top of the original
(all reading exclusively from our own internal TPCH test-data bucket,
s3://ray-benchmark-data/tpch/parquet/sf{N}/lineitem -- no external or
public dataset is used anywhere in this change), inspired by (but not
copying any data/code/infra from) delta-io/delta's own benchmark suite,
which tests data loading at both a small "smoke" scale and a large
scale:
- write_delta_smoke: SF10 (small, already used elsewhere in this file)
on a single-node fixed_size_1_cpu_compute.yaml cluster (also already
used elsewhere in this file as a smoke tier), for fast, cheap nightly
feedback on the basic write_delta path without waiting for the
expensive SF1000 run.
- write_delta_overwrite: SF100, exercising SaveMode.OVERWRITE against
an already-existing table (not just a brand-new one, which the
original entry already covers via APPEND). Populates the table once
first (a separately-reported "setup_populate" Benchmark.run_fn step)
so the measured "main" step genuinely replaces existing data of
realistic size, rather than trivially creating an empty table --
SF100 (not SF1000) keeps this variant's now-doubled read/write cost
proportionate to the existing SF1000 test rather than doubling it.
- write_delta_partitioned: SF1000, Hive-partitioned by column08 (this
S3 dataset stores lineitem columns positionally, not by TPCH name;
column08 is l_returnflag per tpch/common.py's TABLE_COLUMNS mapping
-- a 3-value, low-cardinality column, safe at this scale), exercising
the partitioned-write code path at full scale, which the original
entry doesn't touch at all.
read_and_consume_benchmark.py gains two new --write-delta modifier
flags (--write-delta-mode, --write-delta-partition-by; validated to
require --write-delta) to drive these variants without duplicating the
consume_fn logic. No changes needed to benchmark.py: Benchmark.run_fn
already supports multiple named calls per script run (its own docstring
documents exactly this pattern), which is what the overwrite variant's
setup_populate/main split relies on.
Verified locally end-to-end (local Parquet source, local write_delta
destination, no S3/cluster access): the original single-write path by
invoking the modified script's main() directly with a constructed
args.Namespace; the three new variants via a dedicated smoke test
confirming (a) the CLI validation guard rejects the new modifier flags
without --write-delta, (b) SaveMode.OVERWRITE against an
already-populated table results in the original row count, not double
(i.e. genuinely overwritten, not appended), and (c)
--write-delta-partition-by produces the expected Hive-style
column08=<value> directories and reads back correctly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Abhishek Verma <abhishek.verma@anyscale.com>
3506500 to
49c22f9
Compare


Summary
Adds a
write_deltacounterpart to the existingwrite_parquetrelease benchmark,reusing the same real dataset and harness rather than inventing new synthetic-data
infrastructure:
read_and_consume_benchmark.py: adds a--write-deltaconsume option, mirroringthe existing
--write(write_parquet) option exactly — same read path, sameBenchmark/BenchmarkMetricreporting, just a different write call.release_data_tests.yaml: registers awrite_deltatest entry alongsidewrite_parquet, reading the same TPCH SF1000lineitemsource(
s3://ray-benchmark-data/tpch/parquet/sf1000/lineitem), so the two are a direct,apples-to-apples Parquet-vs-Delta write comparison on identical input data.
byod_install_deltalake.sh: new post-build script installingdeltalake==1.5.0(matching the version pinned in
python/requirements_compiled*.txt), neededbecause — unlike parquet/image/tfrecords —
deltalakeisn't in the baserelease-test image. Mirrors
byod_install_pyiceberg.sh's exact shape.Test plan
python -m py_compileon the modified script — passes.ruff check/black --check(repo-pinned versions) on the modified script —clean.
shellcheck(via pre-commit) on the new byod script — clean.release_data_tests.yamlwithyaml.safe_loadand confirmed the newwrite_deltaentry parses correctly and appears exactly once.source, invoked the modified script's
main()directly with a constructedargs.Namespace(--format parquet --write-deltaequivalent) against a localwrite_deltadestination, confirmed theBenchmarkharness times and reports therun, and confirmed the resulting Delta table reads back correctly via
ray.data.read_delta(...).AI assistance disclosure
This PR was developed with AI assistance (Claude). All changes were reviewed and are
defended by the submitting human.