Consider the scenario where you have a server that handler requests via a request handler (refer to picture). The handler (via its workers) can take a long time, so you need a timeout just in case it never terminates. Requests that take a long time are acceptable in our case, as long as any one of the workers is progressing. However, the initial timeout that we set limits this. (I.e. is there a way to detect inactivity instead of a constant timeout?)
For instance, you have 6 workers working under a context containing a timeout of 5 seconds. Worker n takes n seconds to complete (e.g. worker 3 takes 3 seconds to finish).
Since we have a timeout for 5 seconds, the 6th worker will timeout. So, my question is, can you change this to refresh the timer at a later point during execution (e.g. when a worker finished or decides that it is progressing)?
Refer to image in the post below
In concrete terms, you can take a look at this program:
package main
import (
"context"
"fmt"
"sync"
"time"
)
func main() {
fmt.Println("handling request")
workers := 10
var wg sync.WaitGroup
wg.Add(workers)
timeout := 5 * time.Second
ctx, _ := context.WithTimeout(context.Background(), timeout)
for i := 1; i <= workers; i++ {
go worker(ctx, i, &wg)
}
wg.Wait()
fmt.Println("All workers terminated")
}
// Worker will take index * time.Second to return
func worker(ctx context.Context, index int, wg *sync.WaitGroup) {
defer wg.Done()
// fmt.Println("Worker ", index, "waiting for ", index, "seconds")
select {
case <-ctx.Done():
// Cancel worker
fmt.Println("Worker ", index, "timed out")
return
case <-time.After(time.Duration(index) * time.Second):
// Process finished after some time
fmt.Println("Worker ", index, "finished")
}
}
It outputs:
handling request
Worker 1 finished
Worker 2 finished
Worker 3 finished
Worker 4 finished
Worker 5 finished
Worker 6 timed out
All workers terminated
How can I allow worker 6 to finish, since it will still be within the 5 seconds of the last inactive go routine.