-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtick.go
189 lines (160 loc) · 4.38 KB
/
tick.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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
package gomet
import (
"time"
)
// One tick represents the aggregated stats for specified time duration.
// The tick contains all goroutine groups and channels measured with metrics.
// The each tick's info could be saved to DB or file or printed on screen or whatever you need.
type Tick struct {
Time time.Time
Period time.Duration
Groups map[string]Group
}
func newTick(tm time.Time, period time.Duration) Tick {
return Tick{
Time: tm,
Period: period,
Groups: make(map[string]Group),
}
}
func (t Tick) set(group string, wid int64, state string, start time.Time, dur time.Duration, inProgress bool) {
g, ok := t.Groups[group]
if !ok {
g = make(Group)
}
w, ok := g[wid]
if !ok {
w = make(Worker)
}
s := w[state].next(dur)
if inProgress {
s.Incomplete = dur
}
if s.Count == 1 && t.Time.Sub(start) > t.Period {
s.Age = t.Time.Sub(start) - t.Period
s.Duration -= s.Age
}
w[state] = s
g[wid] = w
t.Groups[group] = g
}
// Scale is the amount of workers per tick,
// e.g. if we have 2 workers, first is existing during whole tick and second only for half
// then scale will be 1+0.5 = 1.5
// It is shorthand for tick.Groups[group].Scale(tick.Period)
func (t Tick) Scale(group string) float32 {
return t.Groups[group].Scale(t.Period)
}
// Count returns number of times that group was in particular state during period
// It is shorthand for t.Groups[group].Count(state)
func (t Tick) Count(group, state string) int {
return t.Groups[group].Count(state)
}
// Load returns average load of sate for workers group
// load means the percent of time spent for the state during tick period
// It is shorthand for tick.Groups[group].Load(state, tick.Period)
func (t Tick) Load(group, state string) float32 {
return t.Groups[group].Load(state)
}
// Lasted returns average time that state needs to get completed
// It is shorthand for tick.Groups[group].Lasted(state)
func (t Tick) Lasted(group, state string) time.Duration {
return t.Groups[group].Lasted(state)
}
//---------------------------------------
// Group contains summarized stats for goroutines group or channel
// It also contains collection of all workers from group
type Group map[int64]Worker
// Sacle is the amount of workers per tick,
// e.g. if we have 2 workers, first is existing during whole tick and second only for half
// then scale will be 1+0.5 = 1.5
func (g Group) Scale(period time.Duration) float32 {
return float32(g.lifeSum()) / float32(period)
}
// calculates sum of workers live time during period
func (g Group) lifeSum() (dur time.Duration) {
for _, w := range g {
for _, s := range w {
dur += s.Duration
}
}
return dur
}
// Count return count of times group been in particular state
func (g Group) Count(state string) int {
cnt := int64(0)
for _, w := range g {
s, ok := w[state]
if !ok {
continue
}
cnt += s.Count
}
return int(cnt)
}
// Load returns average load of sate for workers group
// load means the percent of time spent for the state
func (g Group) Load(state string) float32 {
if len(g) == 0 {
return 0
}
var dur time.Duration
for _, w := range g {
s, ok := w[state]
if !ok {
continue
}
dur += s.Duration
}
lifeSum := g.lifeSum()
if lifeSum == 0 {
return 0
}
return float32(dur) / float32(lifeSum)
}
// Count returns number of times that group was in particular state during period
func (g Group) Lasted(state string) time.Duration {
cnt := int64(0)
var dur time.Duration
for _, w := range g {
s, ok := w[state]
if !ok {
continue
}
if s.Incomplete == 0 {
dur += s.Duration + s.Age
cnt += s.Count
}
}
if cnt == 0 {
return 0
}
return dur / time.Duration(cnt)
}
// Worker contains stats for one goroutine or one transfer through channel
type Worker map[string]State
// State represent time info such as
// average time percent for the tick
type State struct {
Count int64
Min, Max time.Duration
// time spent in previous periods
Age time.Duration
// time spent since event start or period start
Duration time.Duration
// incomplete contains duration of state that is in progress by the end of the tick
// if it is 0 it means that worker not in this state by the end of aggregate period
Incomplete time.Duration
}
// next modify state stat and returns new state.
func (s State) next(dur time.Duration) State {
s.Count++
s.Duration += dur
if s.Min == 0 || s.Min > dur {
s.Min += dur
}
if s.Max < dur {
s.Max = dur
}
return s
}