in client/concurrency.go [112:173]
func sendConcurrentRequests(sendFn func() error) error {
// Get a benchmark for the time it takes for a single request
singleReqTime, singleReqErr := timeExecution(func() error {
return sendFn()
})
if singleReqErr != nil {
return fmt.Errorf("concurrent validation unable to send single request to benchmark response time: %v", singleReqErr)
}
minWait := 1 * time.Second
if singleReqTime < minWait {
return fmt.Errorf("concurrent validation requires a function that waits at least %s before responding, function responded in %s", minWait, singleReqTime)
}
log.Printf("Single request response time benchmarked, took %s for 1 request", singleReqTime)
// Get a benchmark for the time it takes for concurrent requests
const numConReqs = 10
log.Printf("Starting %d concurrent workers to send requests", numConReqs)
type workerResponse struct {
id int
err error
}
var wg sync.WaitGroup
respCh := make(chan workerResponse, numConReqs)
conReqTime, _ := timeExecution(func() error {
for i := 0; i < numConReqs; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
err := sendFn()
respCh <- workerResponse{id: id, err: err}
}(i)
}
wg.Wait()
return nil
})
maybeErrMessage := ""
for i := 0; i < numConReqs; i++ {
resp := <-respCh
if resp.err != nil {
maybeErrMessage += fmt.Sprintf("error #%d: %v\n", i, resp.err)
} else {
log.Printf("Worker #%d done", resp.id)
}
}
if maybeErrMessage != "" {
return fmt.Errorf("at least one concurrent request failed:\n%s", maybeErrMessage)
}
// Validate that the concurrent requests were handled faster than if all
// the requests were handled serially, using the single request time
// as a benchmark. Some buffer is provided by doubling the single request time.
if conReqTime > 2*singleReqTime {
return fmt.Errorf("function took too long to complete %d concurrent requests. %d concurrent request time: %s, single request time: %s", numConReqs, numConReqs, conReqTime, singleReqTime)
}
log.Printf("Concurrent request response time benchmarked, took %s for %d requests", conReqTime, numConReqs)
return nil
}