Simplify main fetching/sorting logic
Reduced the amount of go-routines. Now sorting is done in the same go-routine as URLs are fetched. Then these sorted lists are merged one by one in a loop as they arrive. This greatly reduces complexity.
This commit is contained in:
parent
ebbcd71b3d
commit
9476a00648
@ -62,25 +62,29 @@ func numbersHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-blocking input channel for URL results.
|
// Non-blocking channel for sorted results
|
||||||
// We will read as much as we can at once.
|
sortChan := make(chan []int, len(rurl))
|
||||||
inputChan := make(chan []int, len(rurl))
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
// fetch all URLs asynchronously
|
// fetch all URLs asynchronously and sort them
|
||||||
for i := range rurl {
|
for i := range rurl {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(url string) {
|
go func(url string) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
n, e := getNumbers(url, ctx)
|
n, e := getNumbers(url, ctx)
|
||||||
|
|
||||||
if e == nil {
|
if e == nil { // we have a usable JSON list of numbers
|
||||||
if n != nil && len(n) > 0 {
|
if n != nil && len(n) > 0 { // the list of numbers is non-empty
|
||||||
inputChan <- n
|
sorted, err := sort.SortedAndDedup(ctx, n)
|
||||||
} else {
|
if err != nil { // sorting threw an error
|
||||||
|
log.Printf("Sorting took too long, ignoring list")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sortChan <- sorted
|
||||||
|
} else { // empty list of numbers
|
||||||
log.Printf("Received empty list of numbers from endpoint")
|
log.Printf("Received empty list of numbers from endpoint")
|
||||||
}
|
}
|
||||||
} else {
|
} else { // no usable JSON/other error
|
||||||
log.Printf("Got an error: %s", e)
|
log.Printf("Got an error: %s", e)
|
||||||
}
|
}
|
||||||
}(rurl[i])
|
}(rurl[i])
|
||||||
@ -88,69 +92,33 @@ func numbersHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
// master routine closing the inputChan
|
// master routine closing the inputChan
|
||||||
go func() {
|
go func() {
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
close(inputChan)
|
close(sortChan)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// channel for sorting process, so we can short-circuit in
|
// result list that is returned on short-circuit
|
||||||
// case sorting takes too long
|
var resultList []int = []int{}
|
||||||
sortChan := make(chan []int, 1)
|
|
||||||
// aggregate numbers from URLs
|
|
||||||
var numberBuffer []int = []int{}
|
|
||||||
// these are actually sorted
|
|
||||||
var sortedNumbers []int = []int{}
|
|
||||||
|
|
||||||
// aggregate and sort loop,
|
// get all sorted lists concurrently and merge them as we go
|
||||||
// breaks if all URLs have been processed or the timeout
|
|
||||||
// has been reached
|
|
||||||
done := false
|
done := false
|
||||||
for done != true {
|
for done != true {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
log.Printf("Waiting for URL took too long, finishing response anyway")
|
log.Printf("Waiting for URL took too long, finishing response anyway")
|
||||||
finishResponse(w, sortedNumbers)
|
finishResponse(w, resultList)
|
||||||
return
|
return
|
||||||
case res, more := <-inputChan:
|
case res, more := <-sortChan:
|
||||||
if more { // still URLs to fetch
|
if more {
|
||||||
numberBuffer = append(numberBuffer, res...)
|
merged := sort.MergeLists(resultList, res)
|
||||||
// continue to aggregate numbers from the buffer
|
resultList = merged
|
||||||
continue
|
} else {
|
||||||
} else { // all URLs fetched, sort and be done
|
log.Printf("Nothing else to sort")
|
||||||
log.Printf("Nothing else to fetch")
|
|
||||||
done = true
|
done = true
|
||||||
}
|
}
|
||||||
// non-blocking branch that sorts what we already have
|
|
||||||
// we are not done here yet
|
|
||||||
default:
|
|
||||||
// only sort if we have new results
|
|
||||||
if len(numberBuffer) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sort fallthrough, either the inputChan is currently "empty"
|
|
||||||
// or we fetched all URLs already
|
|
||||||
go func(sorted []int, unsorted_buffer []int) {
|
|
||||||
res, err := sort.SortedAndDedup(ctx, unsorted_buffer)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
merged := sort.MergeLists(sortedNumbers, res)
|
|
||||||
sortChan <- merged
|
|
||||||
}(sortedNumbers, numberBuffer)
|
|
||||||
numberBuffer = []int{}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case merged := <-sortChan:
|
|
||||||
sortedNumbers = merged
|
|
||||||
case <-ctx.Done():
|
|
||||||
log.Printf("Sorting took too long, finishing response anyway")
|
|
||||||
finishResponse(w, sortedNumbers)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Result is complete, finishing response")
|
log.Printf("Result is complete, finishing response")
|
||||||
finishResponse(w, sortedNumbers)
|
finishResponse(w, resultList)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalizes the JSON response with the given numbers. This always
|
// Finalizes the JSON response with the given numbers. This always
|
||||||
|
Loading…
Reference in New Issue
Block a user