fix(backend): cap gh/git fork concurrency via shared subproc throttles (#1216)
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user