/
latency.go
103 lines (85 loc) · 1.92 KB
/
latency.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
package awsregions
import (
"math/rand"
"net/http"
"sort"
"sync"
"time"
)
type measurement struct {
region string
latency time.Duration
}
type LatencyChecker struct {
regions []string
latencies map[string]time.Duration
sync.RWMutex
}
func NewLatencyChecker(regions ...string) LatencyChecker {
return LatencyChecker{regions: regions, latencies: map[string]time.Duration{}}
}
func (lc *LatencyChecker) Start() {
receiver := make(chan measurement)
for _, region := range lc.regions {
go keepMeasuring(region, receiver)
}
for {
select {
case m := <-receiver:
lc.store(m)
}
}
}
func (lc *LatencyChecker) FastestRegion() string {
return lc.SortedRegions()[0]
}
func (lc *LatencyChecker) SortedRegions() []string {
dup := make([]string, len(lc.regions))
lc.RLock()
copy(dup, lc.regions)
lc.RUnlock()
return dup
}
func (lc *LatencyChecker) Latencies() map[string]time.Duration {
dup := map[string]time.Duration{}
lc.RLock()
for k, v := range lc.latencies {
dup[k] = v
}
lc.RUnlock()
return dup
}
func (lc *LatencyChecker) store(m measurement) {
lc.Lock()
lc.latencies[m.region] = (m.latency + lc.latencies[m.region]) / 2
sort.Slice(lc.regions, func(i, j int) bool {
return lc.latencies[lc.regions[i]] < lc.latencies[lc.regions[j]]
})
lc.Unlock()
}
func (lc *LatencyChecker) Measure() {
wg := sync.WaitGroup{}
for _, region := range lc.SortedRegions() {
wg.Add(1)
go func(rg string) {
lc.store(measure(rg))
wg.Done()
}(region)
}
wg.Wait()
}
func keepMeasuring(region string, receiver chan measurement) {
receiver <- measure(region)
for {
receiver <- measure(region)
time.Sleep(time.Duration(rand.Int63n(30)) * time.Second)
}
}
func measure(region string) measurement {
start := time.Now()
resp, _ := http.Get("https://dynamodb." + region + ".amazonaws.com")
defer resp.Body.Close()
end := time.Now()
latency := end.Sub(start)
return measurement{region: region, latency: latency}
}