-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Timeout execution tree creation for SDK worker ops. #36200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4fd4824
8410a06
95ee282
8d352f8
b7b038e
9eea568
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,6 +24,7 @@ | |
| import base64 | ||
| import bisect | ||
| import collections | ||
| import concurrent.futures | ||
| import copy | ||
| import heapq | ||
| import itertools | ||
|
|
@@ -76,6 +77,7 @@ | |
| from apache_beam.runners.worker import operation_specs | ||
| from apache_beam.runners.worker import operations | ||
| from apache_beam.runners.worker import statesampler | ||
| from apache_beam.runners.worker.worker_status import thread_dump | ||
| from apache_beam.transforms import TimeDomain | ||
| from apache_beam.transforms import core | ||
| from apache_beam.transforms import environments | ||
|
|
@@ -1130,7 +1132,30 @@ def __init__( | |
| 'fnapi-step-%s' % self.process_bundle_descriptor.id, | ||
| self.counter_factory) | ||
|
|
||
| self.ops = self.create_execution_tree(self.process_bundle_descriptor) | ||
| with concurrent.futures.ThreadPoolExecutor( | ||
| max_workers=1, thread_name_prefix='ExecutionTreeCreator') as executor: | ||
| future = executor.submit( | ||
| self.create_execution_tree, self.process_bundle_descriptor) | ||
| try: | ||
| self.ops = future.result(timeout=3600) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| except concurrent.futures.TimeoutError: | ||
| # In rare cases, unpickling a DoFn might get permanently stuck, | ||
| # for example when unpickling involves importing a module and | ||
| # a subprocess is launched during the import operation. | ||
| _LOGGER.error( | ||
| 'Timed out while reconstructing a pipeline fragment for: %s.\n' | ||
| 'This is likely a transient error. The SDK harness ' | ||
| 'will self-terminate, and the runner can retry the operation. ' | ||
| 'If the error is frequent, check whether the stuckness happens ' | ||
| 'while deserializing (unpickling) a dependency of your pipeline ' | ||
| 'in the stacktrace below: \n%s\n', | ||
| self.process_bundle_descriptor.id, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated the message, thanks. |
||
| thread_dump('ExecutionTreeCreator')) | ||
| # Raising an exception here doesn't interrupt the left-over thread. | ||
| # Out of caution, terminate the SDK harness process. | ||
| from apache_beam.runners.worker.sdk_worker_main import terminate_sdk_harness | ||
| terminate_sdk_harness() | ||
|
|
||
| for op in reversed(self.ops.values()): | ||
| op.setup(self.data_sampler) | ||
| self.splitting_lock = threading.Lock() | ||
|
|
||
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.