80 lines
2.2 KiB
Go
80 lines
2.2 KiB
Go
package check
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"sync"
|
|
"sync/atomic"
|
|
|
|
"git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
|
)
|
|
|
|
// ReadWorkers is how many values a read tool asks the bus for at once. Each value is a round trip from the tool through the
|
|
// runtime to the bus, so reading a bucket of thousands of keys one after the other outlasts the 30 s a tool call is given
|
|
// (issue 1827, as the delivery view's issue 1945). It is a copy of the view's: a few lines are not worth a group delivery
|
|
// through mesh-sdk (rules-are-weighed).
|
|
const ReadWorkers = 16
|
|
|
|
// MaxReads bounds the values one call of a read tool reads; past it the answer says it was cut and how to narrow it.
|
|
const MaxReads = 1000
|
|
|
|
// parallel runs read(i) for i in [0, n), at most ReadWorkers at a time, and answers the first error; once one failed no
|
|
// further read starts.
|
|
func parallel(n int, read func(i int) error) error {
|
|
var (
|
|
next atomic.Int64
|
|
failed atomic.Bool
|
|
once sync.Once
|
|
first error
|
|
wg sync.WaitGroup
|
|
)
|
|
for w := 0; w < min(ReadWorkers, n); w++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for !failed.Load() {
|
|
i := int(next.Add(1) - 1)
|
|
if i >= n {
|
|
return
|
|
}
|
|
if err := read(i); err != nil {
|
|
once.Do(func() { first = err })
|
|
failed.Store(true)
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
return first
|
|
}
|
|
|
|
// capKeys keeps the first MaxReads keys and answers a note when it cut the rest, "" otherwise.
|
|
func capKeys(keys []string, narrow string) ([]string, string) {
|
|
if len(keys) <= MaxReads {
|
|
return keys, ""
|
|
}
|
|
return keys[:MaxReads], fmt.Sprintf("read %d of %d matching keys and stopped; %s", MaxReads, len(keys), narrow)
|
|
}
|
|
|
|
// newestFirst sorts step keys (<node>.<id>) by id, newest first: an id is the time in base 36, then six hex digits, so the
|
|
// longer id is the later and equal lengths compare as text. A key that is no step key goes last.
|
|
func newestFirst(keys []string) {
|
|
id := func(k string) (string, bool) {
|
|
_, id, err := buildstep.ParseStepKey(k)
|
|
return id, err == nil
|
|
}
|
|
sort.Slice(keys, func(i, j int) bool {
|
|
a, aok := id(keys[i])
|
|
b, bok := id(keys[j])
|
|
switch {
|
|
case aok != bok:
|
|
return aok
|
|
case len(a) != len(b):
|
|
return len(a) > len(b)
|
|
case a != b:
|
|
return a > b
|
|
}
|
|
return keys[i] > keys[j]
|
|
})
|
|
}
|