Open
Conversation
Contributor
Reviewer's GuideRefactors the logic that downloads and parses missing cloud storage backups to use a ThreadPoolExecutor-based worker instead of manually managed threads, and ensures subprocess and parsing timeouts are correctly applied when reading from a named pipe. Sequence diagram for downloading and parsing missing cloud storage backups via named pipesequenceDiagram
participant GenerateBlobs as _generate_blobs_from_tar_files
participant Executor as ThreadPoolExecutor
participant Parser as _insert_blobs_from_tar
participant Subprocess as ch-backup process
participant Pipe as named_pipe
GenerateBlobs->>GenerateBlobs: missing_backups = get_missing_chs3_backups(disk_conf.name)
GenerateBlobs->>Executor: create with max_workers = 1
loop for each backup in missing_backups
GenerateBlobs->>GenerateBlobs: read config (pipe_path, timeout)
GenerateBlobs->>GenerateBlobs: check os.path.exists(pipe_path)
alt pipe exists
GenerateBlobs-->>GenerateBlobs: raise RuntimeError
else pipe does not exist
GenerateBlobs->>Pipe: create via _missing_backups_named_pipe
GenerateBlobs->>Executor: submit(_insert_blobs_from_tar, pipe_path)
Executor-->>GenerateBlobs: Future
GenerateBlobs->>Subprocess: Popen(ch-backup get-cloud-storage-metadata ... --local-path pipe_path backup)
Subprocess->>Pipe: write tar metadata stream
Subprocess->>Subprocess: communicate(timeout=timeout)
Subprocess-->>GenerateBlobs: (stdout, stderr)
alt subprocess returncode != 0
GenerateBlobs-->>GenerateBlobs: raise RuntimeError with stderr
else subprocess success
GenerateBlobs->>Executor: wait on Future.result(timeout)
alt parser completes before timeout
Parser->>Pipe: read and parse tar contents
Parser-->>Executor: parsing done
Executor-->>GenerateBlobs: result
else parser times out
Executor-->>GenerateBlobs: raise TimeoutError
GenerateBlobs-->>GenerateBlobs: raise RuntimeError for locked metadata reading
end
end
GenerateBlobs->>Pipe: close and remove via context manager
end
end
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Contributor
There was a problem hiding this comment.
Hey - I've found 2 issues, and left some high level feedback:
- The
future.result(timeout)call raisesconcurrent.futures.TimeoutError, not the built-inTimeoutError, so the currentexcept TimeoutError:block will never trigger; catchconcurrent.futures.TimeoutErrorexplicitly instead. - Using a
ThreadPoolExecutor(max_workers=1)that lives for the entire loop but only ever runs a single short-lived task per iteration adds complexity compared to the originalthreading.Thread; consider whether a simple thread with a timeout or a per-iteration executor is clearer for this use case.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `future.result(timeout)` call raises `concurrent.futures.TimeoutError`, not the built-in `TimeoutError`, so the current `except TimeoutError:` block will never trigger; catch `concurrent.futures.TimeoutError` explicitly instead.
- Using a `ThreadPoolExecutor(max_workers=1)` that lives for the entire loop but only ever runs a single short-lived task per iteration adds complexity compared to the original `threading.Thread`; consider whether a simple thread with a timeout or a per-iteration executor is clearer for this use case.
## Individual Comments
### Comment 1
<location path="ch_tools/common/commands/object_storage.py" line_range="548-557" />
<code_context>
+ with ThreadPoolExecutor(max_workers=1) as executor:
</code_context>
<issue_to_address>
**issue (bug_risk):** Potential deadlock when `future.result` times out but executor shutdown waits for the still-running task.
Previously this ran in a daemon thread, so a hung `_insert_blobs_from_tar` wouldn’t block process exit. `ThreadPoolExecutor` workers are non-daemon and the context manager calls `shutdown(wait=True)` by default. If `future.result(timeout=timeout)` times out, the task continues running and the `with ThreadPoolExecutor(...)` exit can block forever. To avoid this, either explicitly use `executor.shutdown(wait=False)` on timeout, manage the executor outside the loop to control shutdown, or revert to a daemon `Thread` so a timeout can’t cause the process to hang.
</issue_to_address>
### Comment 2
<location path="ch_tools/common/commands/object_storage.py" line_range="587-589" />
<code_context>
- raise RuntimeError(
- "Downloading cloud storage metadata command has failed: Timeout exceeded, metadata reading thread is probably locked"
- )
+ try:
+ future.result(timeout)
+ except TimeoutError:
+ raise RuntimeError(
+ "Downloading cloud storage metadata command has failed: Timeout exceeded, metadata reading thread is probably locked"
</code_context>
<issue_to_address>
**issue (bug_risk):** Catching the wrong TimeoutError type from `future.result`.
`future.result(timeout=timeout)` raises `concurrent.futures.TimeoutError`, not the built-in `TimeoutError`, so this `except TimeoutError:` block will never run and the timeout will propagate unhandled. Please catch `concurrent.futures.TimeoutError` instead, either via an explicit import alias or by using the fully qualified name.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
aalexfvk
approved these changes
Mar 26, 2026
Contributor
|
Merge main pls |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary by Sourcery
Bug Fixes: