/
multiget.go
73 lines (56 loc) · 1.53 KB
/
multiget.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package index
import (
"context"
"errors"
"log"
"golang.org/x/sync/errgroup"
)
const debug bool = false
func contextDone(ctx context.Context, err error) bool {
ctxErr := ctx.Err()
if ctxErr != nil {
return errors.Is(err, ctxErr)
}
return false
}
// MultiGet returns `fields` for the first document with `id` from given `indexes`.
// When the document is not found (nil, nil) is returned.
func MultiGet(ctx context.Context, indexes []Index, id string, dst interface{}, fields ...string) (Index, error) {
foundIdx := make(chan Index, 1)
ctx, cancel := context.WithCancel(ctx)
defer cancel() // cancel when we are finished
g, groupCtx := errgroup.WithContext(ctx)
for _, i := range indexes {
i := i // https://go.dev/doc/faq#closures_and_goroutines
g.Go(func() error {
if debug {
log.Printf("MultiGet %s index %s", id, i)
}
found, err := i.Get(groupCtx, id, dst, fields...)
if err != nil && !contextDone(ctx, err) {
// Ignore context done errors if MultiGet context is canceled.
return err
}
if found {
select {
case <-groupCtx.Done():
// Don't attempt to write if our context is closed.
return nil
case foundIdx <- i:
cancel() // Found, we're done.
}
}
return nil
})
}
// Wait blocks until all function calls from the Go method have returned, then returns the first non-nil error (if any) from them.
err := g.Wait()
// Close channel, return resources.
close(foundIdx)
select {
case result := <-foundIdx:
return result, err
default:
return nil, err
}
}