forked from hailocab/go-hostpool
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathhost_entry.go
99 lines (86 loc) · 2.82 KB
/
host_entry.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
package hostpool
import (
"time"
)
// --- hostEntry - this is due to get upgraded
type hostEntry struct {
host string
nextRetry time.Time
retryCount int16
retryDelay time.Duration
dead bool
failures *ringBuffer
epsilonCounts []int64 // ring of counts observed
epsilonValues []int64 // ring of total time observations observed
epsilonIndex int // current index in the ring
epsilonWeightedTotal float64 // The total not including the active bucket
epsilonWeightedLastVal float64 // The last non-zero count average
}
func (h *hostEntry) canTryHost(now time.Time) bool {
if !h.dead {
return true
}
if h.nextRetry.Before(now) {
return true
}
return false
}
func (h *hostEntry) willRetryHost(maxRetryInterval time.Duration) {
h.retryCount += 1
newDelay := h.retryDelay * 2
if newDelay < maxRetryInterval {
h.retryDelay = newDelay
} else {
h.retryDelay = maxRetryInterval
}
h.nextRetry = time.Now().Add(h.retryDelay)
}
func (h *hostEntry) getWeightedAverageResponseTime() float64 {
currentBucketCount := h.epsilonCounts[h.epsilonIndex]
// If we've not seen any observations yet, use the last value from the
// previous buckets
if currentBucketCount == 0 {
return h.epsilonWeightedTotal + h.epsilonWeightedLastVal
}
// Take our weighted total and add on the average of our current index
// which has a 100% weighting
currentAvg := float64(h.epsilonValues[h.epsilonIndex]) / float64(currentBucketCount)
return h.epsilonWeightedTotal + currentAvg
}
func (h *hostEntry) epsilonDecay() {
// Move to the next position in the ring
h.epsilonIndex = (h.epsilonIndex + 1) % len(h.epsilonCounts)
h.epsilonCounts[h.epsilonIndex] = 0
h.epsilonValues[h.epsilonIndex] = 0
h.calculateWeightedAverages()
}
func (h *hostEntry) calculateWeightedAverages() {
// We start with the oldest entry in the ring and move forward, coming up to
// the most recent entry (but not the current one which is when i = 0
// resulting in pos pointing to the current bucket index)
buckets := len(h.epsilonCounts)
var total, lastValue float64
for i := 1; i < buckets; i++ {
pos := (h.epsilonIndex + i) % buckets
bucketCount := h.epsilonCounts[pos]
weight := float64(i) / float64(buckets)
if h.epsilonCounts[pos] > 0 {
// We have observed values in this bucket, so let's tally them up
avg := float64(h.epsilonValues[pos]) / float64(bucketCount)
total += avg * weight
lastValue = avg
} else {
// We had no values observed in this bucket, so we just use the
// previous bucket and carry over the weight
total += lastValue * weight
}
}
h.epsilonWeightedTotal = total
h.epsilonWeightedLastVal = lastValue
}
func (h *hostEntry) markDead(initialRetryDelay time.Duration) {
h.dead = true
h.retryCount = 0
h.retryDelay = initialRetryDelay
h.nextRetry = time.Now().Add(h.retryDelay)
}