-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbalancer.go
107 lines (92 loc) · 2.98 KB
/
balancer.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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package http
import (
"errors"
"sync"
)
var (
ErrNoAddresses = errors.New("no addresses available")
ErrDuplicateAddresses = errors.New("duplicate addresses provided")
)
// Balancer is an interface for selecting which node (address) a request
// should be sent to, plus receiving feedback about whether that request
// ultimately succeeded or failed.
//
// You can implement your own Balancer to incorporate custom logic like
// reading from the nearest node, writing to the leader, or distributing
// load across nodes with specialized heuristics.
type Balancer interface {
// Next returns the next address to try. An implementation might return an
// error if there are no addresses available, or if all known addresses have
// recently failed, etc.
//
// The method could also accept arguments about the request (e.g. read vs.
// write) if you want to route them differently.
Next() (string, error)
// MarkSuccess informs the Balancer that a request to the specified address
// succeeded, allowing the balancer to update any internal metrics, counters,
// or tracking that determines future selections. If addr was not previously
// known to the balancer, the call does nothing.
MarkSuccess(addr string)
// MarkFailure informs the Balancer that a request to the specified address
// failed, letting the balancer mark the address as unhealthy or increment
// an error count. If addr was not previously known to the balancer, the call
// does nothing.
MarkFailure(addr string)
}
// RandomBalancer picks a random address from the list for each call to Next.
type RandomBalancer struct {
addresses map[string]bool
numAvailable int
mu sync.RWMutex
}
// NewRandomBalancer initializes a RandomBalancer given a non-empty list of addresses.
func NewRandomBalancer(addresses []string) (*RandomBalancer, error) {
if len(addresses) == 0 {
return nil, errors.New("must provide at least one address")
}
// Any duplicate addresses would be a mistake in the configuration.
a := make(map[string]bool, len(addresses))
for _, addr := range addresses {
if _, ok := a[addr]; ok {
return nil, ErrDuplicateAddresses
}
a[addr] = true
}
rb := &RandomBalancer{
addresses: a,
numAvailable: len(a),
}
return rb, nil
}
// Next returns one of the known addresses at random.
func (rb *RandomBalancer) Next() (string, error) {
rb.mu.RLock()
defer rb.mu.RUnlock()
if rb.numAvailable == 0 {
return "", errors.New("no addresses available")
}
// return a random address - mock for now
return "", nil
}
// MarkSuccess marks the specified address as healthy.
func (rb *RandomBalancer) MarkSuccess(addr string) {
rb.mu.Lock()
defer rb.mu.Unlock()
a, ok := rb.addresses[addr]
if a || !ok {
return
}
rb.addresses[addr] = true
rb.numAvailable++
}
// MarkFailure marks the specified address as unhealthy.
func (rb *RandomBalancer) MarkFailure(addr string) {
rb.mu.Lock()
defer rb.mu.Unlock()
a, ok := rb.addresses[addr]
if !a || !ok {
return
}
rb.addresses[addr] = false
rb.numAvailable--
}