Merge pull request #31 from towerops-app/fix/pool-shutdown-timeout
fix: add timeout to worker pool shutdown to prevent blocking reconnec…
This commit is contained in:
commit
160055e0c0
3 changed files with 52 additions and 4 deletions
17
agent.go
17
agent.go
|
|
@ -251,10 +251,19 @@ func runSession(ctx context.Context, baseURL, token string) error {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
pools.snmp.stop()
|
const poolShutdownTimeout = 5 * time.Second
|
||||||
pools.mikrotik.stop()
|
if !pools.snmp.stopWithTimeout(poolShutdownTimeout) {
|
||||||
pools.ping.stop()
|
slog.Warn("snmp pool shutdown timed out, abandoning in-flight jobs")
|
||||||
pools.checks.stop()
|
}
|
||||||
|
if !pools.mikrotik.stopWithTimeout(poolShutdownTimeout) {
|
||||||
|
slog.Warn("mikrotik pool shutdown timed out, abandoning in-flight jobs")
|
||||||
|
}
|
||||||
|
if !pools.ping.stopWithTimeout(poolShutdownTimeout) {
|
||||||
|
slog.Warn("ping pool shutdown timed out, abandoning in-flight jobs")
|
||||||
|
}
|
||||||
|
if !pools.checks.stopWithTimeout(poolShutdownTimeout) {
|
||||||
|
slog.Warn("checks pool shutdown timed out, abandoning in-flight jobs")
|
||||||
|
}
|
||||||
close(writeCh)
|
close(writeCh)
|
||||||
writerWg.Wait()
|
writerWg.Wait()
|
||||||
}()
|
}()
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// workerPool is a fixed-size goroutine pool for executing tasks.
|
// workerPool is a fixed-size goroutine pool for executing tasks.
|
||||||
|
|
@ -55,3 +56,20 @@ func (p *workerPool) stop() {
|
||||||
p.wg.Wait()
|
p.wg.Wait()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stopWithTimeout closes the task channel and waits up to timeout for workers
|
||||||
|
// to finish. Returns true if all workers completed, false if the timeout was
|
||||||
|
// reached and some workers were abandoned.
|
||||||
|
func (p *workerPool) stopWithTimeout(timeout time.Duration) bool {
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
p.stop()
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
return true
|
||||||
|
case <-time.After(timeout):
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,27 @@ func TestWorkerPool(t *testing.T) {
|
||||||
pool.stop()
|
pool.stop()
|
||||||
pool.stop() // should not panic
|
pool.stop() // should not panic
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("stopWithTimeout returns true when workers finish in time", func(t *testing.T) {
|
||||||
|
pool := newWorkerPool(2)
|
||||||
|
pool.submit(context.Background(), func() {
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
})
|
||||||
|
if !pool.stopWithTimeout(time.Second) {
|
||||||
|
t.Error("expected stopWithTimeout to return true")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("stopWithTimeout returns false when workers exceed timeout", func(t *testing.T) {
|
||||||
|
pool := newWorkerPool(1)
|
||||||
|
blocker := make(chan struct{})
|
||||||
|
pool.submit(context.Background(), func() { <-blocker })
|
||||||
|
|
||||||
|
if pool.stopWithTimeout(50 * time.Millisecond) {
|
||||||
|
t.Error("expected stopWithTimeout to return false")
|
||||||
|
}
|
||||||
|
close(blocker) // unblock for cleanup
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWorkerPoolRecoversPanic(t *testing.T) {
|
func TestWorkerPoolRecoversPanic(t *testing.T) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue