Skip to content

Timeout execution tree creation for SDK worker ops. - #36200

Merged
tvalentyn merged 6 commits into
apache:masterfrom
tvalentyn:dofn_timeout
Oct 16, 2025
Merged

Timeout execution tree creation for SDK worker ops.#36200
tvalentyn merged 6 commits into
apache:masterfrom
tvalentyn:dofn_timeout

Conversation

@tvalentyn

@tvalentyn tvalentyn commented Sep 18, 2025

Copy link
Copy Markdown
Contributor

In rare cases, unpickling a DoFn might get permanently stuck.

One Beam customer sporadically observed this stuckness in a scenario when unpickling involved importing a module (pygithub?), where the import operation involved launching a subprocess.

This PR adds a 60 min timeout to create bundle processor operations, after which the SDK is terminated. Runner would then restart the work item.

Tested:

image

Thank you for your contribution! Follow this checklist to help us incorporate your contribution quickly and easily:

  • Mention the appropriate issue in your description (for example: addresses #123), if applicable. This will automatically add a link to the pull request in the issue. If you would like the issue to automatically close on merging the pull request, comment fixes #<ISSUE NUMBER> instead.
  • Update CHANGES.md with noteworthy changes.
  • If this contribution is large, please file an Apache Individual Contributor License Agreement.

See the Contributor Guide for more tips on how to make review process smoother.

To check the build health, please visit https://github.com/apache/beam/blob/master/.test-infra/BUILD_STATUS.md

GitHub Actions Tests Status (on master branch)

Build python source distribution and wheels
Python tests
Java tests
Go tests

See CI.md for more information about GitHub Actions CI or the workflows README to see a list of phrases to trigger workflows.

@tvalentyn
tvalentyn marked this pull request as ready for review September 19, 2025 00:14
@tvalentyn

Copy link
Copy Markdown
Contributor Author

R: @liferoad cc: @baeminbo

@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

# a subprocess is launched during the import operation.
_LOGGER.error(
'Timed out when creating execution tree for %s.\n%s',
self.process_bundle_descriptor.id,

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 we terminate this sdk harness later, I think this error should be harmless. So can we mention this somehow?

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.

+1 ", restarting harness to retry and continue processing."

I think you might also want to map this to concepts the user is more familiar with like unpickling and calling their setup method.
"This may indicate issues unpickling or in the dofn setup function."

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.

ping on this part, I think otherwise we could punt on other comments but this seems good to fix.

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.

Updated the message, thanks.

@liferoad liferoad 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.

/gemini review

future = executor.submit(
self.create_execution_tree, self.process_bundle_descriptor)
try:
self.ops = future.result(timeout=3600)

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.

could this be a pipeline option? an hour might be prohibitively long for some cases. Also since this ends up calling user setup function there is possiblity an hour is too short

I think we likely want to add options for how long to cache these bundle processors as well. Currently it is just 60 seconds and then we clear the cache. If this is expensive for streaming pipelines we should evict less aggressively. (that can be a different cl just bringing it up as motivation for a new options class controlling stuff like this)

@tvalentyn tvalentyn Sep 19, 2025

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.

Using an option can be doable, we could tie it to the existing per-element timeout option. Note that DoFn.setup is called a bit later, immediately after execution tree creation. Slow Setup operations will not be causing the timeout here.

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.

I was mostly worried this might break things with very expensive setup, but since that's not included I think we could punt on making it configurable if you just want to cover the unpickling.

It could be nice to cover the setup with a timeout too. I believe we had issues in the past where setup getting stuck was hard to debug since it is covered by the lull logic. An alternative would be to defer calling setup until after lull registration

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.

i am looking at some of those issues since this work is related. I might merge this PR as is before next release but still looking to see if we can get a more general solution here

# a subprocess is launched during the import operation.
_LOGGER.error(
'Timed out when creating execution tree for %s.\n%s',
self.process_bundle_descriptor.id,

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.

+1 ", restarting harness to retry and continue processing."

I think you might also want to map this to concepts the user is more familiar with like unpickling and calling their setup method.
"This may indicate issues unpickling or in the dofn setup function."

self.counter_factory)

self.ops = self.create_execution_tree(self.process_bundle_descriptor)
with concurrent.futures.ThreadPoolExecutor(

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.

If we are going to add timeouts in other places that might be called more frequently, I think we would want some more generic facility to register thread and timeout and then have single background thread observing for timeouts instead of starting a thread for each call. This seems ok here but just something to consider for future.

with WatchdogTimeout(3600, handle_timeout):
  // run code that might get stuck inline on this thread

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.

That looks like a neat construct, good idea.

future = executor.submit(
self.create_execution_tree, self.process_bundle_descriptor)
try:
self.ops = future.result(timeout=3600)

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.

I was mostly worried this might break things with very expensive setup, but since that's not included I think we could punt on making it configurable if you just want to cover the unpickling.

It could be nice to cover the setup with a timeout too. I believe we had issues in the past where setup getting stuck was hard to debug since it is covered by the lull logic. An alternative would be to defer calling setup until after lull registration

# a subprocess is launched during the import operation.
_LOGGER.error(
'Timed out when creating execution tree for %s.\n%s',
self.process_bundle_descriptor.id,

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.

ping on this part, I think otherwise we could punt on other comments but this seems good to fix.

@tvalentyn tvalentyn closed this Sep 26, 2025
@tvalentyn tvalentyn reopened this Sep 26, 2025
@tarun-google

Copy link
Copy Markdown
Contributor

@tvalentyn this PR is stale for a week. Please resolve comments and work with reviewer.

@tvalentyn
tvalentyn merged commit d91fb6d into apache:master Oct 16, 2025
77 of 82 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.

4 participants