diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..4b1e42f --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2026-05-27 - Parallelize gh CLI calls +**Learning:** Subprocess calls to the GitHub CLI ('gh') are a significant performance bottleneck. When checking states for multiple PRs sequentially, it creates an N+1 delay issue. +**Action:** Use concurrency (e.g., `concurrent.futures.ThreadPoolExecutor`) to eliminate sequential delays when making multiple independent subprocess CLI calls (e.g., checking PR open states). Ensure the original order is preserved by using `executor.map`. diff --git a/ralph_loop/cli.py b/ralph_loop/cli.py index da6a240..e117751 100644 --- a/ralph_loop/cli.py +++ b/ralph_loop/cli.py @@ -2,6 +2,7 @@ from __future__ import annotations import argparse +import concurrent.futures import os import re import shlex @@ -544,23 +545,28 @@ def _filter_to_still_open_prs(pr_numbers: List[int]) -> List[int]: swallow stale PRs because of a flaky network. """ kept: List[int] = [] - for pr in pr_numbers: + + def _check_pr(pr: int) -> Tuple[int, bool, Optional[CommandError]]: try: - still_open = _pr_is_still_open(pr) + return pr, _pr_is_still_open(pr), None except CommandError as exc: - _print_step( - "Could not confirm PR #{} open state ({}); keeping it in the " - "fan-out set.".format(pr, exc) - ) - kept.append(pr) - continue - if still_open: - kept.append(pr) - else: - _print_step( - "PR #{} is no longer open (per gh pr view); skipping " - "fan-out spawn.".format(pr) - ) + return pr, True, exc + + with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor: + for pr, still_open, exc in executor.map(_check_pr, pr_numbers): + if exc: + _print_step( + "Could not confirm PR #{} open state ({}); keeping it in the " + "fan-out set.".format(pr, exc) + ) + kept.append(pr) + elif still_open: + kept.append(pr) + else: + _print_step( + "PR #{} is no longer open (per gh pr view); skipping " + "fan-out spawn.".format(pr) + ) return kept