...
1 package utils
2
3 import (
4 "flag"
5 "fmt"
6 "sync"
7 "time"
8 )
9
10 const (
11 COUNTERS = 10
12 )
13
14 var (
15 sliding_avg_debug = flag.Bool("ge_debug_sliding_average", false, "debug sliding avg code")
16 )
17
18
26 type SlidingAverage struct {
27 lock sync.Mutex
28 calc1 *sacalc
29 calc2 *sacalc
30 InitialAge time.Duration
31 MinAge time.Duration
32 MinSamples uint64
33 updating_number_one bool
34 created time.Time
35 created_via_new bool
36 switched bool
37 }
38
39 func NewSlidingAverage() *SlidingAverage {
40 res := &SlidingAverage{
41 InitialAge: time.Duration(24) * time.Hour,
42 created_via_new: true,
43 created: time.Now(),
44 MinAge: time.Duration(10) * time.Second,
45 MinSamples: 10,
46 }
47 return res
48 }
49
50
51 type sacalc struct {
52 debug_name string
53 is_fresh bool
54 started time.Time
55 counts []uint64
56 counter []uint64
57 }
58
59 func new_sacalc(debug_name string) *sacalc {
60 res := &sacalc{
61 debug_name: debug_name,
62 is_fresh: true,
63 counts: make([]uint64, COUNTERS),
64 counter: make([]uint64, COUNTERS),
65 }
66 return res
67 }
68
69
70
71 func (sa *SlidingAverage) check_for_switch() {
72 up := sa.to_be_updated()
73 rd := sa.to_be_read()
74 if sa.meetsCriteria(up) {
75 sa.updating_number_one = !sa.updating_number_one
76 sa.switched = true
77 if rd != nil {
78 rd.make_fresh()
79 }
80 }
81 }
82 func (sa *SlidingAverage) meetsCriteria(sc *sacalc) bool {
83 if time.Since(sc.started) < sa.MinAge {
84 return false
85 }
86 samples := uint64(0)
87 for i := 0; i < COUNTERS; i++ {
88 samples = samples + sc.counts[i]
89 }
90 if samples < sa.MinSamples {
91 return false
92 }
93
94 return true
95 }
96 func (sc *sacalc) make_fresh() {
97 for i := 0; i < COUNTERS; i++ {
98 sc.counts[i] = 0
99 sc.counter[i] = 0
100 }
101 sc.is_fresh = true
102 }
103
104
105 func (sa *SlidingAverage) to_be_updated() *sacalc {
106 if sa.updating_number_one {
107 if sa.calc1 == nil {
108 sa.calc1 = new_sacalc("sacalc-1")
109 }
110 return sa.calc1
111 }
112 if sa.calc2 == nil {
113 sa.calc2 = new_sacalc("sacalc-2")
114 }
115 return sa.calc2
116 }
117 func (sa *SlidingAverage) to_be_read() *sacalc {
118 var alt_res *sacalc
119
120 if sa.updating_number_one {
121 alt_res = sa.calc2
122 } else {
123 alt_res = sa.calc1
124 }
125 if sa.switched || time.Since(sa.created) < sa.InitialAge {
126 return alt_res
127 }
128 if sa.calc1 == nil && sa.calc2 != nil {
129 return sa.calc2
130 }
131 if sa.calc2 == nil && sa.calc1 != nil {
132 return sa.calc1
133 }
134 if sa.calc1 == nil && sa.calc2 == nil {
135 return nil
136 }
137
138 if sa.calc1.is_fresh && !sa.calc2.is_fresh {
139 alt_res = sa.calc2
140 }
141
142 if sa.calc2.is_fresh && !sa.calc1.is_fresh {
143 alt_res = sa.calc1
144 }
145
146 return alt_res
147
148 }
149
150
151 func (sa *SlidingAverage) GetCounts(counter int) uint64 {
152 if !sa.created_via_new {
153 panic("[go-easyops] SlidingAverage must be created with function NewSlidingAverage()")
154 }
155 sa.lock.Lock()
156 defer sa.lock.Unlock()
157 sc := sa.to_be_read()
158 if sc == nil {
159 return 0
160 }
161 return sc.getCounts(counter)
162 }
163
164
165 func (sa *SlidingAverage) GetCounter(counter int) uint64 {
166 if !sa.created_via_new {
167 panic("[go-easyops] SlidingAverage must be created with function NewSlidingAverage()")
168 }
169 sa.lock.Lock()
170 defer sa.lock.Unlock()
171 sc := sa.to_be_read()
172 if sc == nil {
173 return 0
174 }
175 return sc.getCounter(counter)
176 }
177 func (sa *SlidingAverage) GetAverage(counter int) float64 {
178 num := sa.GetCounter(counter)
179 counts := sa.GetCounts(counter)
180 if counts == 0 || num == 0 {
181 return 0
182 }
183 res := float64(num) / float64(counts)
184 sa.printf("result: num=%0.1f, counts=%0.1f -> %0.1f\n", num, counts, res)
185 return res
186 }
187
188
189 func (sa *SlidingAverage) GetRate(counter int) float64 {
190 sa.lock.Lock()
191 defer sa.lock.Unlock()
192 sc := sa.to_be_read()
193 if sc == nil {
194 return 0.0
195 }
196 num := float64(sc.getCounter(counter))
197 secs := time.Since(sc.started).Seconds()
198 if num == 0 || secs == 0 {
199 return 0.0
200 }
201 res := num / secs
202 return res
203 }
204
205 func (sa *SlidingAverage) Add(counter int, a uint64) {
206 if !sa.created_via_new {
207 panic("[go-easyops] SlidingAverage must be created with function NewSlidingAverage()")
208 }
209 sa.lock.Lock()
210 defer sa.lock.Unlock()
211 sa.to_be_updated().Add(counter, a)
212 sa.check_for_switch()
213
214 }
215
216 func (sc *sacalc) Add(counter int, a uint64) {
217 sc.counts[counter]++
218 sc.counter[counter] = sc.counter[counter] + a
219 if sc.is_fresh {
220 sc.started = time.Now()
221 sc.is_fresh = false
222 }
223 sc.Printf("added: %d to counter #%d, now %d\n", a, counter, sc.counter[counter])
224 }
225 func (sc *sacalc) getCounter(counter int) uint64 {
226 if sc == nil {
227 return 0
228 }
229 return sc.counter[counter]
230 }
231 func (sc *sacalc) getCounts(counter int) uint64 {
232 if sc == nil {
233 return 0
234 }
235 return sc.counts[counter]
236 }
237
238 func (sc *sacalc) Printf(format string, args ...interface{}) {
239 if !*sliding_avg_debug {
240 return
241 }
242 s := "[go-easyops " + sc.debug_name + "] " + format
243
244 fmt.Printf(s, args...)
245 }
246 func (sa *SlidingAverage) printf(format string, args ...interface{}) {
247 if !*sliding_avg_debug {
248 return
249 }
250 s := "[go-easyops slidingavg] " + format
251 fmt.Printf(s, args...)
252 }
253
View as plain text