Use case
I'd like to run two queries against a database in parallel and return after a maximum time of 600ms whatever I have fetched to that point. I am struggling with implementing the concurrency for this requirement.
Code
func (s *Service) GetCustomerStats(ctx context.Context, customerUUID string) *CustomerStats {
stats := &CustomerStats{
CustomerUUID: customerUUID,
Type: "ERROR",
OrderCount: "ERROR",
}
var wg sync.WaitGroup
var mu sync.Mutex
// Get order count
wg.Add(1)
go func() {
defer wg.Done()
orderCount, err := s.Storage.GetOrderCount(ctx, customerUUID)
if err != nil {
return
}
mu.Lock()
stats.OrderCount = strconv.Itoa(orderCount)
if orderCount == 0 {
stats.OrderCount = "NA"
}
mu.Unlock()
}()
// Get customer type
wg.Add(1)
go func() {
defer wg.Done()
type, err := s.Storage.GetCustomerType(ctx, customerUUID)
if err != nil {
return
}
mu.Lock()
stats.Type = strconv.Itoa(type)
mu.Unlock()
}()
wg.Wait()
return stats
}
The problem
The context I pass into that function has a timeout of 600ms defined. I pass it on to the storage repo and the Database driver uses it as well, but it does not guarantee it will respond within that time as it does schedule some retries under the hood.
However I must ensure that this function returns within the passed context timeout (600ms). I am currently using a waitgroup to await the results but I wouldn't know how to return stats
once the context is done.
Basically I am looking for something like this. My research indicates that I should probably use channels which signal that the work is done but I am not sure how I would implement that so that it's simple code.
select {
case wg.Wait()
return stats
case <-ctx.Done()
return stats
}