Skip to content

Add in-tool retries with backoff for create_zstream_branch git operations - #581

Merged
lbarcziova merged 2 commits into
packit:mainfrom
lbarcziova:retry-transient-zstream-branch
Jun 9, 2026
Merged

lbarcziova merged 2 commits into
packit:mainfrom
lbarcziova:retry-transient-zstream-branch

Conversation

@lbarcziova

Copy link
Copy Markdown
Member

No description provided.

…ions

The SSH clone, push, and ls_remote calls to bastion/GitLab can fail
transiently due to network blips (connection closed, connection reset,
failed to push some refs). Previously these raised ToolError immediately,
relying on the beeai framework's LLM-driven retry which has no backoff
and is non-deterministic.

Now the three git operations retry up to 3 times with exponential backoff
(5s, 10s, 20s). Transient errors (identified by stderr patterns) are
retried; permanent errors (permission denied, authentication failed, etc.)
fail immediately without wasting attempts.

Assisted-by: Claude Opus 4.6

@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 introduces a retry mechanism for transient Git errors during clone, push, and remote operations, along with comprehensive unit tests. The review feedback suggests wrapping synchronous file system operations (os.path.exists and shutil.rmtree) in asyncio.to_thread to prevent blocking the main event loop.

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 +126 to +129
async def _clone():
if os.path.exists(clone_dest):
shutil.rmtree(clone_dest)
return await asyncio.to_thread(git.Repo.clone_from, clone_url, clone_dest)

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

The helper function _clone performs synchronous file system operations (os.path.exists and shutil.rmtree) directly on the main event loop. For repositories with many files, shutil.rmtree can block the event loop for a significant duration, degrading the performance of other concurrent tasks. Running these operations in a separate thread using asyncio.to_thread avoids blocking the event loop.

Suggested change
async def _clone():
if os.path.exists(clone_dest):
shutil.rmtree(clone_dest)
return await asyncio.to_thread(git.Repo.clone_from, clone_url, clone_dest)
async def _clone():
if await asyncio.to_thread(os.path.exists, clone_dest):
await asyncio.to_thread(shutil.rmtree, clone_dest)
return await asyncio.to_thread(git.Repo.clone_from, clone_url, clone_dest)

_TRANSIENT_MAX_RETRIES = 3
_TRANSIENT_BASE_DELAY = 5 # seconds

_TRANSIENT_STDERR_PATTERNS = (

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.

This seems like a pretty ad-hoc heuristic list. I personally would say it's simpler to just retry on all errors; it's unlikely but possible that some of these error strings change eventually.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are some of the SSH/network error messages we saw in production last wee. My concern with retrying everything is adding unnecessary load on the infra for permanent errors

Comment thread ymir/tools/privileged/distgit.py Outdated
return await fn()
except Exception as e:
if attempt < max_retries - 1 and _is_transient_git_error(e):
backoff = base_delay * 2**attempt

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.

Usually best practice for this is to add an additional randomized delay to reduce "thundering herd".

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, addressed

Review feedback: use randomized delay (full jitter) to prevent
concurrent agents from retrying in lockstep after a shared outage.

Assisted-by: Claude Opus 4.6

@TomasTomecek TomasTomecek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, very nice improvements! I look forward to gather these so we can have concrete numbers and see the state of infra real time

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants