Skip to content

Comments

Improve performance and reliability with native git CLI and better error handling#230

Merged
AnnatarHe merged 2 commits intomainfrom
claude/fix-daemon-performance-54SSH
Feb 13, 2026
Merged

Improve performance and reliability with native git CLI and better error handling#230
AnnatarHe merged 2 commits intomainfrom
claude/fix-daemon-performance-54SSH

Conversation

@AnnatarHe
Copy link
Contributor

@AnnatarHe AnnatarHe commented Feb 13, 2026

Summary

This PR improves daemon performance and reliability through three main changes: replacing the pure-Go git library with native git CLI calls, adding retry logic with exponential backoff for message delivery, and fixing concurrency issues in rate limit fetching.

Key Changes

  • Replace go-git with native git CLI (daemon/git.go)

    • Switched from go-git library to os/exec for git operations
    • Significantly faster and more memory-efficient, especially on large repositories
    • Added 2-second timeout for git commands to prevent hangs
    • Simplified implementation while maintaining the same GitInfo interface
  • Add retry logic for message delivery (daemon/chan.go)

    • Implemented exponential backoff retry mechanism (max 3 retries) for nacked messages
    • Backoff delays: 100ms, 200ms, 400ms
    • Gracefully handles shutdown during backoff to avoid message loss
    • Prevents infinite retry loops that could cause resource exhaustion
  • Fix rate limit fetch concurrency (daemon/cc_info_timer.go)

    • Added rateLimitFetchMu mutex to guard concurrent fetchRateLimit goroutines
    • Used TryLock() to prevent multiple concurrent fetches
    • Added 10-second timeout context for rate limit operations
    • Prevents race conditions and duplicate API calls
  • Improve socket connection handling (daemon/socket.go)

    • Restructured acceptConnections() loop to check stop signal only on errors
    • Added 5-second deadline to socket connections to prevent indefinite hangs
    • Better separation of concerns between accept errors and shutdown

Implementation Details

  • Git operations now use context with timeout to prevent hanging on unresponsive repositories
  • Message retry backoff uses exponential backoff formula: 100ms * 2^(retryCount-1)
  • Rate limit fetch uses non-blocking lock acquisition to skip if already running
  • Socket deadline is set per-connection to handle slow clients gracefully

https://claude.ai/code/session_0128SDwXB8JYiSP8kyRjmSgw


Open with Devin

- Fix socket accept busy loop: replace non-blocking select/default with
  blocking Accept(), checking stopChan only on error to prevent 100% CPU
  spin when Accept() returns transient errors
- Replace go-git worktree.Status() with native git CLI commands (rev-parse,
  status --porcelain) with 2s timeout, eliminating ~500MB memory from
  pure-Go tree walking on large repos
- Add 5s read deadline on socket connections to prevent goroutine leaks
  from clients that connect but never send data
- Guard fetchRateLimit goroutine with TryLock to prevent unbounded
  goroutine accumulation when HTTP requests exceed 3s tick interval
- Add exponential backoff (100/200/400ms) and max retry (3) to pub/sub
  Nack handling to prevent CPU-burning retry loops on persistent failures

https://claude.ai/code/session_0128SDwXB8JYiSP8kyRjmSgw
@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @AnnatarHe, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the daemon's performance and reliability by migrating Git operations to the native Git CLI for improved efficiency. It also introduces a robust exponential backoff retry mechanism for message delivery to ensure resilience against transient failures. Furthermore, concurrency issues in rate limit fetching have been addressed, and socket connection handling has been refined for better resource management and stability.

Highlights

  • Replaced go-git with native git CLI: Switched from the go-git library to os/exec for git operations, leading to significantly faster and more memory-efficient performance, especially on large repositories. A 2-second timeout was also added for git commands.
  • Added retry logic for message delivery: Implemented an exponential backoff retry mechanism with a maximum of 3 retries for nacked messages (delays: 100ms, 200ms, 400ms). This includes graceful handling of shutdowns during backoff to prevent message loss.
  • Fixed rate limit fetch concurrency: Introduced a rateLimitFetchMu mutex with TryLock() to guard concurrent fetchRateLimit goroutines, preventing race conditions and duplicate API calls. A 10-second timeout context was also added for these operations.
  • Improved socket connection handling: Restructured the acceptConnections() loop to check the stop signal only on errors and added a 5-second deadline to individual socket connections to prevent indefinite hangs.
Changelog
  • daemon/cc_info_timer.go
    • Added a rateLimitFetchMu mutex to prevent concurrent execution of fetchRateLimit.
    • Wrapped fetchRateLimit calls with TryLock() and a 10-second context timeout to manage concurrency and prevent hangs.
  • daemon/chan.go
    • Imported fmt and time packages for retry logic.
    • Implemented a retry mechanism for nacked messages with a maximum of 3 retries.
    • Introduced exponential backoff (100ms, 200ms, 400ms) for retries.
    • Added logic to discard messages if max retries are reached or if the daemon is closing during backoff.
  • daemon/git.go
    • Imported context, os/exec, strings, and time packages.
    • Defined gitCmdTimeout constant as 2 seconds.
    • Replaced go-git library calls with os/exec commands for git rev-parse --git-dir, git rev-parse --abbrev-ref HEAD, and git status --porcelain.
    • Added context with timeout to all git commands for better control.
  • daemon/socket.go
    • Modified the acceptConnections loop to check the stopChan only when listener.Accept() returns an error, improving shutdown handling.
    • Added conn.SetDeadline(time.Now().Add(5 * time.Second)) to handleConnection to prevent indefinite hangs for slow clients.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

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 significantly improves the daemon's performance and reliability by replacing go-git with native git CLI calls, adding robust retry logic for message delivery, and fixing a concurrency issue in rate limit fetching. The changes are well-implemented and address the stated goals effectively. My review includes a few suggestions to further enhance maintainability and robustness, such as refactoring duplicated code, replacing magic numbers with constants, and improving error handling in the socket connection loop.

Comment on lines +131 to +137
if err != nil {
select {
case <-p.stopChan:
return
default:
}
go p.handleConnection(conn)
continue
Copy link
Contributor

Choose a reason for hiding this comment

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

high

If p.listener.Accept() returns an error that is not due to the listener being closed (e.g., a temporary system error like running out of file descriptors), this loop could become a busy-loop, consuming high CPU. To make this more robust, consider handling specific errors. You could check if the error is net.ErrClosed to return from the function, and for other errors, log them and perhaps add a small delay to prevent spinning.

Comment on lines +189 to +197
go func() {
if !s.rateLimitFetchMu.TryLock() {
return
}
defer s.rateLimitFetchMu.Unlock()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
s.fetchRateLimit(ctx)
}()
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This block of code for fetching the rate limit is a duplicate of the logic at lines 166-174. To improve maintainability and follow the DRY (Don't Repeat Yourself) principle, consider extracting this logic into a new private method on CCInfoTimerService. You could then call this new method as a goroutine from both locations.

s.logger.Error("Max retries reached, dropping message", logFields)
return
}
backoff := time.Duration(100<<uint(retryCount-1)) * time.Millisecond
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The value 100 used for the initial backoff is a magic number. To improve readability and maintainability, it's better to define it as a named constant, for example: const initialBackoffMillis = 100. The code would then be more self-explanatory: backoff := time.Duration(initialBackoffMillis<<uint(retryCount-1)) * time.Millisecond.


func (p *SocketHandler) handleConnection(conn net.Conn) {
defer conn.Close()
conn.SetDeadline(time.Now().Add(5 * time.Second))
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The 5-second connection deadline is a magic number. It would be better to define this as a named constant at the package level, like const connectionDeadline = 5 * time.Second, to improve readability and make it easier to configure in the future.

Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

View 6 additional findings in Devin Review.

Open in Devin Review

Fix off-by-one in max retries check and add missing error argument to logger.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@codecov
Copy link

codecov bot commented Feb 13, 2026

Codecov Report

❌ Patch coverage is 60.00000% with 18 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
daemon/chan.go 14.28% 12 Missing ⚠️
daemon/cc_info_timer.go 71.42% 2 Missing and 2 partials ⚠️
daemon/socket.go 77.77% 2 Missing ⚠️
Flag Coverage Δ
unittests 37.56% <60.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
daemon/git.go 100.00% <100.00%> (ø)
daemon/socket.go 61.06% <77.77%> (-1.24%) ⬇️
daemon/cc_info_timer.go 84.04% <71.42%> (-0.86%) ⬇️
daemon/chan.go 79.79% <14.28%> (-4.08%) ⬇️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@AnnatarHe AnnatarHe merged commit 0818da4 into main Feb 13, 2026
3 checks passed
@AnnatarHe AnnatarHe deleted the claude/fix-daemon-performance-54SSH branch February 13, 2026 13:38
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.

2 participants