Skip to content

Fail fast in artifact staging when storing an artifact fails#39367

Open
Eliaaazzz wants to merge 1 commit into
apache:masterfrom
Eliaaazzz:fix-39364-artifact-staging-fail-fast
Open

Fail fast in artifact staging when storing an artifact fails#39367
Eliaaazzz wants to merge 1 commit into
apache:masterfrom
Eliaaazzz:fix-39364-artifact-staging-fail-fast

Conversation

@Eliaaazzz

Copy link
Copy Markdown
Contributor

Fixes #39364.

On Windows, StoreArtifact.call() fails in its first statement with an unchecked InvalidPathException, because the staged filename embeds the environment id and Java environment ids contain colons (beam:env:embedded:v1). The catch block only handled IOException | InterruptedException, so the failure was never reported anywhere: the queue consumer died silently, the gRPC onNext thread eventually blocked forever in OverflowingSemaphore.aquire (or in currentOutput.put once the 100-slot chunk queue filled), the reverse-retrieval stream was neither completed nor errored, and the submitting client waited forever in ArtifactStagingService.offer. This is what makes every portable Java ValidatesRunner test on Windows hang for its full 1200s JUnit timeout during pipeline submission.

This PR makes any artifact storage failure fail fast and reach the client:

  • StoreArtifact.call() reports every exception to totalPendingBytes, including unchecked ones, before rethrowing.
  • OverflowingSemaphore.aquire observes a set exception even when permits are still available, instead of only after the 100 MB budget is exhausted.
  • The chunk producer no longer does an unconditional BlockingQueue.put; it polls with a timeout and rechecks the semaphore's exception, so a dead consumer cannot strand it.
  • The GETCHUNK error handler terminates the stream with Status.INTERNAL carrying the root cause description, instead of only flipping internal state, so offer completes exceptionally with the actual error.

The regression test injects a destination provider that throws InvalidPathException (as LocalResourceId.resolve does on Windows) and streams an artifact in more chunks than the service buffers, so staging cannot run to completion before the failure is observed. Without the main change the test hangs and fails by timeout, reproducing the reported bug; with it, offer fails within seconds and the client sees the root cause.

#39363 complements this fix by sanitizing the Windows-illegal characters out of the generated filenames. With both, portable Java pipelines can stage artifacts on Windows, and any future staging failure will surface immediately instead of hanging the submission.

Tested with gradlew :runners:java-fn-execution:test --tests "*ArtifactStagingServiceTest*": both tests pass, and with the main change reverted the new test fails by timeout as described in the issue.

…hanging

A StoreArtifact task that failed with an unchecked exception, such as
InvalidPathException on Windows, was never reported: the chunk producer
blocked forever and the reverse-retrieval stream was never terminated,
so clients hung in ArtifactStagingService.offer until their timeout.
Report every failure to the pending-bytes semaphore, make the producer
observe it promptly, and error the stream with the root cause.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical issue where artifact staging on Windows would hang indefinitely due to unhandled exceptions during path resolution. By ensuring that all exceptions are properly propagated and that communication channels are resilient to consumer failures, the changes guarantee that pipeline submission fails fast with a clear error message when staging issues occur.

Highlights

  • Improved Error Handling: Updated artifact staging to catch and report all exceptions, including unchecked ones, to prevent silent failures and hanging threads.
  • Semaphore and Queue Robustness: Modified the OverflowingSemaphore and chunk producer to poll for exceptions, ensuring that dead consumers do not cause the producer to block indefinitely.
  • Stream Termination: Ensured that artifact staging failures explicitly terminate the gRPC stream with an internal error, allowing the client to receive the root cause immediately.
  • Regression Testing: Added a new test case that simulates destination path failures to verify that the staging service fails fast instead of timing out.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request prevents the artifact staging service from hanging indefinitely when an exception (such as an unchecked exception during destination resolution) occurs. It introduces a polling mechanism for queue writes, catches general exceptions to set the failure status, and adds a unit test to verify this behavior. The review feedback highlights two important issues: first, calling stagingExecutor.shutdownNow() will permanently disable the shared executor service for future requests; second, catching a general Exception can swallow InterruptedException without restoring the thread's interrupted status.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +435 to +436
state = State.ERROR;
stagingExecutor.shutdownNow();

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.

critical

Calling stagingExecutor.shutdownNow() will shut down the shared executor service of the ArtifactStagingService instance. Since ArtifactStagingService is typically a long-running gRPC service (e.g., in a JobServer), shutting down this executor will permanently prevent any future artifact staging requests from succeeding.

Since the StoreArtifact task has already failed and terminated (which is what set the exception on totalPendingBytes and triggered this catch block), there is no need to shut down or cancel anything in the executor. We can safely remove this call.

Suggested change
state = State.ERROR;
stagingExecutor.shutdownNow();
state = State.ERROR;

Comment on lines 296 to 301
if (exn instanceof IOException) {
throw (IOException) exn;
} else if (exn instanceof RuntimeException) {
throw (RuntimeException) exn;
} else {
throw new RuntimeException(exn);

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.

medium

When catching Exception (which includes InterruptedException), the interrupted status of the thread is cleared. If the exception is an InterruptedException, we should restore the interrupted status by calling Thread.currentThread().interrupt() before rethrowing or wrapping it, so that calling code or the thread pool can handle the interruption properly.

        if (exn instanceof IOException) {
          throw (IOException) exn;
        } else if (exn instanceof InterruptedException) {
          Thread.currentThread().interrupt();
          throw new RuntimeException(exn);
        } else if (exn instanceof RuntimeException) {
          throw (RuntimeException) exn;
        } else {
          throw new RuntimeException(exn);
        }

@github-actions

Copy link
Copy Markdown
Contributor

Assigning reviewers:

R: @chamikaramj 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

Copy link
Copy Markdown
Contributor

Assigning reviewers:

R: @chamikaramj 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).

11 similar comments
@github-actions

Copy link
Copy Markdown
Contributor

Assigning reviewers:

R: @chamikaramj 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

Copy link
Copy Markdown
Contributor

Assigning reviewers:

R: @chamikaramj 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

Copy link
Copy Markdown
Contributor

Assigning reviewers:

R: @chamikaramj 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

Copy link
Copy Markdown
Contributor

Assigning reviewers:

R: @chamikaramj 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

Copy link
Copy Markdown
Contributor

Assigning reviewers:

R: @chamikaramj 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

Copy link
Copy Markdown
Contributor

Assigning reviewers:

R: @chamikaramj 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

Copy link
Copy Markdown
Contributor

Assigning reviewers:

R: @chamikaramj 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

Copy link
Copy Markdown
Contributor

Assigning reviewers:

R: @chamikaramj 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

Copy link
Copy Markdown
Contributor

Assigning reviewers:

R: @chamikaramj 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

Copy link
Copy Markdown
Contributor

Assigning reviewers:

R: @chamikaramj 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

Copy link
Copy Markdown
Contributor

Assigning reviewers:

R: @chamikaramj 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).

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.

[Bug]: Portable Java pipelines hang forever in artifact staging on Windows, staged file name embeds environment id colons

1 participant