-
Notifications
You must be signed in to change notification settings - Fork 796
/
replication_set.go
87 lines (77 loc) · 1.88 KB
/
replication_set.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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package ring
import (
"context"
"time"
)
// ReplicationSet describes the ingesters to talk to for a given key, and how
// many errors to tolerate.
type ReplicationSet struct {
Ingesters []IngesterDesc
MaxErrors int
}
// Do function f in parallel for all replicas in the set, erroring is we exceed
// MaxErrors and returning early otherwise.
func (r ReplicationSet) Do(ctx context.Context, delay time.Duration, f func(*IngesterDesc) (interface{}, error)) ([]interface{}, error) {
var (
errs = make(chan error, len(r.Ingesters))
resultsChan = make(chan interface{}, len(r.Ingesters))
minSuccess = len(r.Ingesters) - r.MaxErrors
done = make(chan struct{})
forceStart = make(chan struct{}, r.MaxErrors)
)
defer func() {
close(done)
}()
for i := range r.Ingesters {
go func(i int, ing *IngesterDesc) {
// wait to send extra requests
if i >= minSuccess && delay > 0 {
after := time.NewTimer(delay)
defer after.Stop()
select {
case <-done:
return
case <-forceStart:
case <-after.C:
}
}
result, err := f(ing)
if err != nil {
errs <- err
} else {
resultsChan <- result
}
}(i, &r.Ingesters[i])
}
var (
numErrs int
numSuccess int
results = make([]interface{}, 0, len(r.Ingesters))
)
for numSuccess < minSuccess {
select {
case err := <-errs:
numErrs++
if numErrs > r.MaxErrors {
return nil, err
}
// force one of the delayed requests to start
forceStart <- struct{}{}
case result := <-resultsChan:
numSuccess++
results = append(results, result)
case <-ctx.Done():
return nil, ctx.Err()
}
}
return results, nil
}
// Includes returns whether the replication set includes the replica with the provided addr.
func (r ReplicationSet) Includes(addr string) bool {
for _, instance := range r.Ingesters {
if instance.GetAddr() == addr {
return true
}
}
return false
}