-
Notifications
You must be signed in to change notification settings - Fork 64
/
loadtest.go
144 lines (114 loc) · 3.69 KB
/
loadtest.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
package loadtester
import (
"context"
"fmt"
"os"
"os/signal"
"sync/atomic"
"time"
"github.com/project-flogo/core/trigger"
)
// Based off github.com/tsliwowicz/go-wrk
// RequesterStats used for collection aggregate statistics
type RequesterStats struct {
TotDuration time.Duration
MinRequestTime time.Duration
MaxRequestTime time.Duration
NumRequests int
NumErrs int
}
type LoadTest struct {
duration int //seconds
concurrencyLevel int
statsAggregator chan *RequesterStats
interrupted int32
}
func NewLoadTest(duration, concurrencyLevel int) *LoadTest {
lt := &LoadTest{duration: duration, concurrencyLevel: concurrencyLevel}
lt.statsAggregator = make(chan *RequesterStats, concurrencyLevel)
return lt
}
func (lt *LoadTest) Run(handler trigger.Handler, data interface{}) error {
fmt.Printf("Running %vs test\n %v goroutine(s) running concurrently\n", lt.duration, lt.concurrencyLevel)
lt.statsAggregator = make(chan *RequesterStats, lt.concurrencyLevel)
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt)
session := NewLoadSession(lt.duration, lt.statsAggregator)
for i := 0; i < lt.concurrencyLevel; i++ {
go session.Run(handler, data)
}
responders := 0
aggStats := RequesterStats{MinRequestTime: time.Minute}
for responders < lt.concurrencyLevel {
select {
case <-sigChan:
session.Stop()
case stats := <-lt.statsAggregator:
aggStats.NumErrs += stats.NumErrs
aggStats.NumRequests += stats.NumRequests
aggStats.TotDuration += stats.TotDuration
aggStats.MaxRequestTime = MaxDuration(aggStats.MaxRequestTime, stats.MaxRequestTime)
aggStats.MinRequestTime = MinDuration(aggStats.MinRequestTime, stats.MinRequestTime)
responders++
}
}
if aggStats.NumRequests == 0 {
fmt.Println("Error: No statistics collected")
return nil
}
avgThreadDur := aggStats.TotDuration / time.Duration(responders) //need to average the aggregated duration
reqRate := float64(aggStats.NumRequests) / avgThreadDur.Seconds()
avgReqTime := aggStats.TotDuration / time.Duration(aggStats.NumRequests)
fmt.Printf("%v requests in %v\n", aggStats.NumRequests, avgThreadDur)
fmt.Printf("Requests/sec:\t\t%.2f\n", reqRate)
fmt.Printf("Avg Req Time:\t\t%v\n", avgReqTime)
fmt.Printf("Fastest Request:\t%v\n", aggStats.MinRequestTime)
fmt.Printf("Slowest Request:\t%v\n", aggStats.MaxRequestTime)
fmt.Printf("Number of Errors:\t%v\n", aggStats.NumErrs)
return nil
}
type LoadSession struct {
duration int //seconds
statsAggregator chan *RequesterStats
interrupted int32
}
func NewLoadSession(duration int, statsAggregator chan *RequesterStats) *LoadSession {
return &LoadSession{duration: duration, statsAggregator: statsAggregator}
}
func (lt *LoadSession) Run(handler trigger.Handler, data interface{}) {
stats := &RequesterStats{MinRequestTime: time.Minute}
sessionStart := time.Now()
for time.Since(sessionStart).Seconds() <= float64(lt.duration) && atomic.LoadInt32(<.interrupted) == 0 {
var duration time.Duration
duration = -1
start := time.Now()
_, err := handler.Handle(context.Background(), data)
duration = time.Since(start)
if err == nil {
stats.TotDuration += duration
stats.MaxRequestTime = MaxDuration(duration, stats.MaxRequestTime)
stats.MinRequestTime = MinDuration(duration, stats.MinRequestTime)
stats.NumRequests++
} else {
stats.NumErrs++
}
}
lt.statsAggregator <- stats
}
func (lt *LoadSession) Stop() {
atomic.StoreInt32(<.interrupted, 1)
}
func MaxDuration(d1 time.Duration, d2 time.Duration) time.Duration {
if d1 > d2 {
return d1
} else {
return d2
}
}
func MinDuration(d1 time.Duration, d2 time.Duration) time.Duration {
if d1 < d2 {
return d1
} else {
return d2
}
}