Skip to content

Make ValidatesRunner faster: Share and cache GCS staging across TestDataflowRunner executions - #40321

Merged
reuvenlax merged 3 commits into
apache:masterfrom
reuvenlax:improve_test_cache_staging_location
Sep 29, 2026
Merged

reuvenlax merged 3 commits into
apache:masterfrom
reuvenlax:improve_test_cache_staging_location

Conversation

@reuvenlax

Copy link
Copy Markdown
Contributor

TestDataflowRunner previously set tempLocation to //output/results, causing DataflowPipelineOptions.StagingLocationFactory` to derive a unique path for every single test job. Consequently, every test re-hashed ~300+ MB of classpath jars and re-queried/uploaded them to a brand-new GCS staging directory instead of reusing staged artifacts across tests.
Fix: Defaults stagingLocation to a shared /staging/ directory across jobs in the same test run (while keeping tempLocation isolated per job at //output/results).

Caches SHA-256 file hashes and directory zip artifacts in memory keyed by (path, size, lastModifiedMillis) so unmodified classpath jars are hashed once per worker JVM instead of once per test.

Caches resolved DataflowPackage lists per `stagingLocation, filesToStage) when running under TestDataflowRunner so each worker JVM stages/verifies the classpath once.

public List<DataflowPackage> stageFiles(List<StagedFile> filesToStage) {
String stagingLocation = options.getStagingLocation();
if (stagingLocation != null && TestDataflowRunner.class.equals(options.getRunner())) {
List<Object> cacheKey = Arrays.asList(stagingLocation, filesToStage);

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.

since the key is a pair of (String, List), consider use it as STAGED_FILES_CACHE's key type. One can use guava's com.google.common.collect.Maps.immutableEntry or org.apache.commons.lang3.tuple.Pair

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

"TestDataflowRunner#TestAppName", TestDataflowRunner.fromOptions(options).toString());
}

@Test

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.

(Not directly due to this PR) Running the test alone (./gradlew :runners:google-cloud-dataflow-java:test --tests *testFromOptionsUsesSharedStagingLocationUnderTempRoot) it fails with

java.lang.IllegalArgumentException: No filesystem found for scheme gs
	at org.apache.beam.sdk.io.FileSystems.getFileSystemInternal(FileSystems.java:558)
	at org.apache.beam.sdk.io.FileSystems.matchNewResource(FileSystems.java:665)
	at org.apache.beam.sdk.io.FileSystems.matchNewDirectory(FileSystems.java:678)
	at org.apache.beam.runners.dataflow.TestDataflowRunner.fromOptions(TestDataflowRunner.java:78)
	at org.apache.beam.runners.dataflow.TestDataflowRunnerTest.testFromOptionsUsesSharedStagingLocationUnderTempRoot(TestDataflowRunnerTest.java:108)

but running the test class passed. Likely a pre-existing issue though.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

taking a look....

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

it's because when doing this only TestDataflowRunner.fromOptions(options) is called and never Pipeline.create(options). Pipeline.create(options) registers the filesystems as a side effect.

Fixed

}

private static final ConcurrentHashMap<StagedFilesCacheKey, List<DataflowPackage>>
STAGED_FILES_CACHE = new ConcurrentHashMap<>();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe use a Cache with a size bound that will be big enough for any reasonable situation, but will prevent future weirdness if things change.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

@Override
public List<DataflowPackage> stageFiles(List<StagedFile> filesToStage) {
String stagingLocation = options.getStagingLocation();
if (stagingLocation != null && TestDataflowRunner.class.equals(options.getRunner())) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why depend on the runner here? Is there a reason it cannot just be always on?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

unlikely to help for other runners (since it's rare to reuse the same JVM for multiple pipelines), however sure - removed this guard.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

fewer deps and lower cyclomatic complexity are also worthwhile :-)

@github-actions

Copy link
Copy Markdown
Contributor

Assigning reviewers:

R: @tvalentyn added as fallback since no labels match configuration

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 tvalentyn September 29, 2026 14:43
@kennknowles

Copy link
Copy Markdown
Member

R: @kennknowles

@github-actions

Copy link
Copy Markdown
Contributor

Stopping reviewer notifications for this pull request: review requested by someone other than the bot, ceding control. If you'd like to restart, comment assign set of reviewers

@reuvenlax
reuvenlax merged commit fa34c16 into apache:master Sep 29, 2026
22 of 23 checks passed
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.

3 participants