fix(backend): cap gh/git fork concurrency via shared subproc throttles (#1216)

This commit is contained in:
Carlos Florêncio
2026-06-01 20:35:43 +01:00
committed by GitHub
parent fd79d6170f
commit a8db7478ac
42 changed files with 2328 additions and 332 deletions
+38
View File
@@ -94,10 +94,32 @@ type Service struct {
// in different goroutines, and the map is shared between them.
cleanupFailureMu sync.Mutex
cleanupFailureCounts map[string]int
// inflightWorkspaceRefreshes tracks workspaces whose stale-PR background
// refresh is currently running, so overlapping ListWorkspaceTaskPRs polls
// from the frontend coalesce instead of stacking goroutines (each of
// which fires its own batched GraphQL request). Keys are workspaceID;
// presence means "a refresh is in flight". sync.Map is the right shape
// here because keys are unbounded and short-lived and the hot path is a
// LoadOrStore guard, not iteration.
inflightWorkspaceRefreshes sync.Map
// stopCtx / stopCancel / bgWG own the lifecycle of background goroutines
// the service spawns lazily (currently refreshStaleWorkspaceWatches).
// Stop() cancels stopCtx and waits for bgWG to drain, satisfying the
// "long-running goroutine has a single owner with explicit start/stop"
// convention in apps/backend/AGENTS.md. stopOnce makes Stop idempotent
// so the Provide cleanup func can be invoked from any number of
// shutdown paths.
stopCtx context.Context
stopCancel context.CancelFunc
bgWG sync.WaitGroup
stopOnce sync.Once
}
// NewService creates a new GitHub service.
func NewService(client Client, authMethod string, secrets SecretProvider, store *Store, eventBus bus.EventBus, log *logger.Logger) *Service {
stopCtx, stopCancel := context.WithCancel(context.Background())
return &Service{
client: client,
authMethod: authMethod,
@@ -112,9 +134,25 @@ func NewService(client Client, authMethod string, secrets SecretProvider, store
protectionCache: newBranchProtectionCache(),
rateTracker: NewRateTracker(eventBus, log),
cleanupFailureCounts: make(map[string]int),
stopCtx: stopCtx,
stopCancel: stopCancel,
}
}
// Stop cancels in-flight background goroutines (currently the
// per-workspace stale-PR refreshes spawned by refreshStaleWorkspaceWatches)
// and waits for them to drain. Idempotent: safe to call from the Provide
// cleanup func and any other shutdown path.
func (s *Service) Stop() {
if s == nil {
return
}
s.stopOnce.Do(func() {
s.stopCancel()
s.bgWG.Wait()
})
}
// RateTracker exposes the service's rate-limit tracker so factory callers
// can wire it into individual clients.
func (s *Service) RateTracker() *RateTracker {