Add TestPoller_ShutdownForcesJobsOnTimeout to lock in the Shutdown()
fix: when a task is parked on jobsCtx and the Shutdown context's
deadline elapses before graceful drain, the timeout branch must reach
p.shutdownJobs() and force-cancel the job — rather than blocking on
<-p.done as the previous (broken) implementation did.
Per @silverwind's review on #822.
- Introduce TaskRunner interface to decouple Poller from concrete run.Runner
- Add TestPoller_ConcurrencyLimitedByCapacity verifying max concurrent
tasks respects capacity and FetchTask is never called concurrently
- Mock runner respects context cancellation for proper shutdown testing
Previously, capacity=N spawned N independent polling goroutines, each
making FetchTask RPCs to the Gitea server concurrently. This caused
unnecessary connection load on the server proportional to the runner's
capacity setting.
Replace the N-goroutine model with a single polling loop that uses a
buffered channel as a semaphore to control concurrent task execution.
The poller acquires a capacity slot before fetching; when at capacity,
it blocks without issuing RPCs. Fetched tasks are dispatched to
independent goroutines that release their slot on completion.
Also fix a pre-existing bug in Shutdown() where the timeout branch
used a blocking receive on p.done instead of a non-blocking select,
which prevented shutdownJobs() from ever being called on timeout.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>