Skip to content

Parallelize per-file rewrites in GcsUtilV2 copy and move - #40376

Merged
shunping merged 3 commits into
apache:masterfrom
shunping:gcs-migration-8
Oct 7, 2026
Merged

shunping merged 3 commits into
apache:masterfrom
shunping:gcs-migration-8

Conversation

@shunping

@shunping shunping commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

GcsUtilV2.rewriteHelper previously processed files sequentially, making up to three dependent RPCs per file (target lookup, rewrite, and source delete). Unlike V1, which batches rewrites and runs batches concurrently, V2's java-storage client does not support rewrite in StorageBatch, causing rename to be ~6x slower than V1 in 64-shard ParquetIOLT benchmarks.

  • GcsUtilV2: extract per-file logic into rewriteOne and execute multi-file copy/move calls concurrently on a bounded thread pool (up to 32 threads). Propagate the caller's MetricsContainer to worker threads, cancel unstarted tasks on the first failure, and rethrow the original exception type (FileNotFoundException, FileAlreadyExistsException, AccessDeniedException, etc.) with subsequent failures attached as suppressed.
  • GcsUtilV2Test: add testV2RenameRewritesFilesConcurrently and testV2RenameFailuresAreReportedPerFile.

In the 64-shard ParquetIOLT (f1_s1k) write benchmark on Dataflow, V2 gcs_op_rename_msec dropped from ~20.6s to ~1.0s (vs ~2.3s–2.9s on V1) after this change.

@shunping
shunping marked this pull request as ready for review October 1, 2026 14:17
@shunping

shunping commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator Author

cc'ed @chamikaramj @damccorm

This addressed the performance regression of rename op in GcsUtilV2 by parallelizing the rewrites.

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Checks are failing. Will not request review until checks are succeeding. If you'd like to override that behavior, comment assign set of reviewers

@shunping

shunping commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator Author

assign set of reviewers

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Assigning reviewers:

R: @ahmedabu98 for label java.

This pull request likely touches a core component ("core" label). Please review with scrutiny.

Note: If you would like to opt out of this review, comment assign to next reviewer.

Available commands:

  • stop reviewer notifications - opt out of the automated review tooling
  • remind me after tests pass - tag the comment author after tests pass
  • waiting on author - shift the attention set back to the author (any comment or push by the author will return the attention set to the reviewers)

The PR bot will only process comments in the main thread (not review comments).

@github-actions
github-actions Bot requested a review from ahmedabu98 October 1, 2026 17:11
@shunping
shunping requested review from chamikaramj and removed request for ahmedabu98 October 1, 2026 17:40
private static final long MEGABYTES_COPIED_PER_CHUNK = 2048L;

/** Maximum number of files rewritten concurrently by {@link #copy} and {@link #move}. */
@VisibleForTesting static final int MAX_CONCURRENT_REWRITES = 32;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe configurable via GcsOptions?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

if (failure == null) {
failure = cause;
for (Future<Void> other : futures) {
other.cancel(false);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not true?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to wait for the inflight rewrite to finish to limit the scope of partial results. With the current code, every file ends up either fully rewritten or never started, with none in an unknown state.

If we call cancel(true) to an on-going RewriteOne call, there is no guarantee that whether the file has already been copied, or in the move case, whether the original file has been removed or not.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revised the code to better handling shutdown on executor. Waiting to push the change but github remote server is not healthy right now.

- Make the number of concurrent copy/move rewrites in GcsUtilV2 configurable
via GcsOptions (defaults to 32).
- Deprecate gcsRewriteDataOpBatchLimit,
which only applies to GcsUtilV1.
When one rewrite fails, files not yet started are cancelled, but the
executor's shutdownNow() also interrupted rewrites already in flight, and
the method could return while they were still running. Wait up to 5 minutes.

@stankiewicz stankiewicz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@shunping
shunping merged commit e26d6ba into apache:master Oct 7, 2026
20 checks passed
@codecov

codecov Bot commented Oct 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 47.87234% with 49 lines in your changes missing coverage. Please review.
✅ Project coverage is 58.73%. Comparing base (9685063) to head (2614804).
⚠️ Report is 70 commits behind head on master.

Files with missing lines Patch % Lines
...apache/beam/sdk/extensions/gcp/util/GcsUtilV2.java 47.87% 38 Missing and 11 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master   #40376      +/-   ##
============================================
+ Coverage     58.65%   58.73%   +0.07%     
- Complexity    13843    13916      +73     
============================================
  Files          2579     2582       +3     
  Lines        270366   270550     +184     
  Branches      11212    11243      +31     
============================================
+ Hits         158587   158900     +313     
+ Misses       105747   105643     -104     
+ Partials       6032     6007      -25     
Flag Coverage Δ
java 65.25% <47.87%> (+0.17%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants