1 | 1 |
|
2 | 2 |
import (
|
3 | 3 |
"io"
|
|
4 |
"sort"
|
4 | 5 |
"sync"
|
|
6 |
"sync/atomic"
|
5 | 7 |
|
6 | 8 |
"github.com/go-kit/kit/endpoint"
|
7 | 9 |
"github.com/go-kit/kit/log"
|
|
18 | 20 |
//
|
19 | 21 |
// EndpointCache is designed to be used in your publisher implementation.
|
20 | 22 |
type EndpointCache struct {
|
21 | |
mtx sync.RWMutex
|
|
23 |
mtx sync.Mutex
|
22 | 24 |
f Factory
|
23 | 25 |
m map[string]endpointCloser
|
|
26 |
cache atomic.Value //[]endpoint.Endpoint
|
24 | 27 |
logger log.Logger
|
25 | 28 |
}
|
26 | 29 |
|
|
28 | 31 |
// strings will be converted to endpoints via the provided factory function.
|
29 | 32 |
// The logger is used to log errors.
|
30 | 33 |
func NewEndpointCache(f Factory, logger log.Logger) *EndpointCache {
|
31 | |
return &EndpointCache{
|
|
34 |
endpointCache := &EndpointCache{
|
32 | 35 |
f: f,
|
33 | 36 |
m: map[string]endpointCloser{},
|
34 | 37 |
logger: log.NewContext(logger).With("component", "Endpoint Cache"),
|
35 | 38 |
}
|
|
39 |
|
|
40 |
endpointCache.cache.Store(make([]endpoint.Endpoint, 0))
|
|
41 |
|
|
42 |
return endpointCache
|
36 | 43 |
}
|
37 | 44 |
|
38 | 45 |
type endpointCloser struct {
|
|
48 | 55 |
defer t.mtx.Unlock()
|
49 | 56 |
|
50 | 57 |
// Produce the current set of endpoints.
|
51 | |
m := make(map[string]endpointCloser, len(instances))
|
|
58 |
oldMap := t.m
|
|
59 |
t.m = make(map[string]endpointCloser, len(instances))
|
52 | 60 |
for _, instance := range instances {
|
53 | 61 |
// If it already exists, just copy it over.
|
54 | |
if ec, ok := t.m[instance]; ok {
|
55 | |
m[instance] = ec
|
56 | |
delete(t.m, instance)
|
|
62 |
if ec, ok := oldMap[instance]; ok {
|
|
63 |
t.m[instance] = ec
|
|
64 |
delete(oldMap, instance)
|
57 | 65 |
continue
|
58 | 66 |
}
|
59 | 67 |
|
|
63 | 71 |
t.logger.Log("instance", instance, "err", err)
|
64 | 72 |
continue
|
65 | 73 |
}
|
66 | |
m[instance] = endpointCloser{endpoint, closer}
|
|
74 |
t.m[instance] = endpointCloser{endpoint, closer}
|
67 | 75 |
}
|
68 | 76 |
|
|
77 |
t.refreshCache()
|
|
78 |
|
69 | 79 |
// Close any leftover endpoints.
|
70 | |
for _, ec := range t.m {
|
|
80 |
for _, ec := range oldMap {
|
71 | 81 |
if ec.Closer != nil {
|
72 | 82 |
ec.Closer.Close()
|
73 | 83 |
}
|
74 | 84 |
}
|
|
85 |
}
|
75 | 86 |
|
76 | |
// Swap and GC.
|
77 | |
t.m = m
|
|
87 |
func (t *EndpointCache) refreshCache() {
|
|
88 |
var (
|
|
89 |
length = len(t.m)
|
|
90 |
instances = make([]string, 0, length)
|
|
91 |
newCache = make([]endpoint.Endpoint, 0, length)
|
|
92 |
)
|
|
93 |
|
|
94 |
for instance, _ := range t.m {
|
|
95 |
instances = append(instances, instance)
|
|
96 |
}
|
|
97 |
// Sort the instances for ensuring that Endpoints are returned into the same order if no modified.
|
|
98 |
sort.Strings(instances)
|
|
99 |
|
|
100 |
for _, instance := range instances {
|
|
101 |
newCache = append(newCache, t.m[instance].Endpoint)
|
|
102 |
}
|
|
103 |
|
|
104 |
t.cache.Store(newCache)
|
78 | 105 |
}
|
79 | 106 |
|
80 | 107 |
// Endpoints returns the current set of endpoints in undefined order. Satisfies
|
81 | 108 |
// Publisher interface.
|
82 | 109 |
func (t *EndpointCache) Endpoints() ([]endpoint.Endpoint, error) {
|
83 | |
t.mtx.RLock()
|
84 | |
defer t.mtx.RUnlock()
|
85 | |
a := make([]endpoint.Endpoint, 0, len(t.m))
|
86 | |
for _, ec := range t.m {
|
87 | |
a = append(a, ec.Endpoint)
|
88 | |
}
|
89 | |
return a, nil
|
|
110 |
return t.cache.Load().([]endpoint.Endpoint), nil
|
90 | 111 |
}
|