1
1
package main
2
2
3
3
import (
4
+ "context"
4
5
"flag"
5
6
"fmt"
6
7
"io/ioutil"
@@ -16,7 +17,6 @@ import (
16
17
17
18
var (
18
19
resultChan = make (chan Result )
19
- abortChan = make (chan bool )
20
20
stopWaitLoop = false
21
21
tearDownInProgress = false
22
22
randomSource = rand .New (rand .NewSource (time .Now ().UnixNano ()))
@@ -25,16 +25,14 @@ var (
25
25
publisherClientIdTemplate = "mqtt-stresser-pub-%s-worker%d-%d"
26
26
topicNameTemplate = "internal/mqtt-stresser/%s/worker%d-%d"
27
27
28
- opTimeout = 5 * time .Second
29
-
30
28
errorLogger = log .New (os .Stderr , "ERROR: " , log .Lmicroseconds | log .Ltime | log .Lshortfile )
31
29
verboseLogger = log .New (os .Stderr , "DEBUG: " , log .Lmicroseconds | log .Ltime | log .Lshortfile )
32
30
33
31
argNumClients = flag .Int ("num-clients" , 10 , "Number of concurrent clients" )
34
32
argNumMessages = flag .Int ("num-messages" , 10 , "Number of messages shipped by client" )
35
- argTimeout = flag .String ("timeout" , "5s" , "Timeout for pub/sub loop " )
33
+ argTimeout = flag .String ("timeout" , "5s" , "Timeout for pub/sub actions " )
36
34
argGlobalTimeout = flag .String ("global-timeout" , "60s" , "Timeout spanning all operations" )
37
- argRampUpSize = flag .Int ("rampup-size" , 100 , "Size of rampup batch" )
35
+ argRampUpSize = flag .Int ("rampup-size" , 100 , "Size of rampup batch. Default rampup batch size is 100. " )
38
36
argRampUpDelay = flag .String ("rampup-delay" , "500ms" , "Time between batch rampups" )
39
37
argTearDownDelay = flag .String ("teardown-delay" , "5s" , "Graceperiod to complete remaining workers" )
40
38
argBrokerUrl = flag .String ("broker" , "" , "Broker URL" )
47
45
argHelp = flag .Bool ("help" , false , "Show help" )
48
46
)
49
47
50
- type Worker struct {
51
- WorkerId int
52
- BrokerUrl string
53
- Username string
54
- Password string
55
- Nmessages int
56
- Timeout time.Duration
57
- }
58
-
59
48
type Result struct {
60
49
WorkerId int
61
50
Event string
@@ -67,6 +56,11 @@ type Result struct {
67
56
ErrorMessage error
68
57
}
69
58
59
+ type TimeoutError interface {
60
+ Timeout () bool
61
+ Error () string
62
+ }
63
+
70
64
func main () {
71
65
flag .Parse ()
72
66
@@ -83,18 +77,23 @@ func main() {
83
77
84
78
if err != nil {
85
79
fmt .Printf ("Could not create CPU profile: %s\n " , err )
80
+ os .Exit (1 )
86
81
}
87
82
88
83
if err := pprof .StartCPUProfile (f ); err != nil {
89
84
fmt .Printf ("Could not start CPU profile: %s\n " , err )
85
+ os .Exit (1 )
90
86
}
91
87
}
92
88
93
89
num := * argNumMessages
94
- brokerUrl := * argBrokerUrl
95
90
username := * argUsername
96
91
password := * argPassword
97
- testTimeout , _ := time .ParseDuration (* argTimeout )
92
+ actionTimeout , err := time .ParseDuration (* argTimeout )
93
+ if err != nil {
94
+ fmt .Printf ("Could not parse '--timeout': '%s' is not a valid duration string. See https://golang.org/pkg/time/#ParseDuration for valid duration strings\n " , * argGlobalTimeout )
95
+ os .Exit (1 )
96
+ }
98
97
99
98
verboseLogger .SetOutput (ioutil .Discard )
100
99
errorLogger .SetOutput (ioutil .Discard )
@@ -107,7 +106,8 @@ func main() {
107
106
verboseLogger .SetOutput (os .Stderr )
108
107
}
109
108
110
- if brokerUrl == "" {
109
+ if * argBrokerUrl == "" {
110
+ fmt .Println ("'--broker' is empty. Abort." )
111
111
os .Exit (1 )
112
112
}
113
113
@@ -123,41 +123,49 @@ func main() {
123
123
124
124
resultChan = make (chan Result , * argNumClients * * argNumMessages )
125
125
126
- for cid := 0 ; cid < * argNumClients ; cid ++ {
126
+ globalTimeout , err := time .ParseDuration (* argGlobalTimeout )
127
+ if err != nil {
128
+ fmt .Printf ("Could not parse '--global-timeout': '%s' is not a valid duration string. See https://golang.org/pkg/time/#ParseDuration for valid duration strings\n " , * argGlobalTimeout )
129
+ os .Exit (1 )
130
+ }
131
+ testCtx , cancelFunc := context .WithTimeout (context .Background (), globalTimeout )
132
+
133
+ stopStartLoop := false
134
+ for cid := 0 ; cid < * argNumClients && ! stopStartLoop ; cid ++ {
127
135
128
136
if cid % rampUpSize == 0 && cid > 0 {
129
137
fmt .Printf ("%d worker started - waiting %s\n " , cid , rampUpDelay )
130
138
time .Sleep (rampUpDelay )
139
+ select {
140
+ case <- time .NewTimer (rampUpDelay ).C :
141
+ case s := <- signalChan :
142
+ fmt .Printf ("Got signal %s. Cancel test.\n " , s .String ())
143
+ cancelFunc ()
144
+ stopStartLoop = true
145
+ }
131
146
}
132
147
133
148
go (& Worker {
134
- WorkerId : cid ,
135
- BrokerUrl : brokerUrl ,
136
- Username : username ,
137
- Password : password ,
138
- Nmessages : num ,
139
- Timeout : testTimeout ,
140
- }).Run ()
149
+ WorkerId : cid ,
150
+ BrokerUrl : * argBrokerUrl ,
151
+ Username : username ,
152
+ Password : password ,
153
+ NumberOfMessages : num ,
154
+ Timeout : actionTimeout ,
155
+ }).Run (testCtx )
141
156
}
142
157
fmt .Printf ("%d worker started\n " , * argNumClients )
143
158
144
159
finEvents := 0
145
160
146
- timeout := make (chan bool , 1 )
147
- globalTimeout , _ := time .ParseDuration (* argGlobalTimeout )
148
161
results := make ([]Result , * argNumClients )
149
162
150
- go func () {
151
- time .Sleep (globalTimeout )
152
- timeout <- true
153
- }()
154
-
155
163
for finEvents < * argNumClients && ! stopWaitLoop {
156
164
select {
157
165
case msg := <- resultChan :
158
166
results [msg .WorkerId ] = msg
159
167
160
- if msg .Event == "Completed" || msg .Error {
168
+ if msg .Event == CompletedEvent || msg .Error {
161
169
finEvents ++
162
170
verboseLogger .Printf ("%d/%d events received\n " , finEvents , * argNumClients )
163
171
}
@@ -167,7 +175,7 @@ func main() {
167
175
}
168
176
169
177
if * argHideProgress == false {
170
- if msg .Event == "Completed" {
178
+ if msg .Event == CompletedEvent {
171
179
fmt .Print ("." )
172
180
}
173
181
@@ -176,16 +184,19 @@ func main() {
176
184
}
177
185
}
178
186
179
- case <- timeout :
180
- fmt .Println ()
181
- fmt .Printf ("Aborted because global timeout (%s) was reached.\n " , * argGlobalTimeout )
182
-
183
- go tearDownWorkers ()
184
- case signal := <- signalChan :
185
- fmt .Println ()
186
- fmt .Printf ("Received %s. Aborting.\n " , signal )
187
-
188
- go tearDownWorkers ()
187
+ case <- testCtx .Done ():
188
+ switch testCtx .Err ().(type ) {
189
+ case TimeoutError :
190
+ fmt .Println ("Test timeout. Wait 5s to allow disconnection of clients." )
191
+ default :
192
+ fmt .Println ("Test canceled. Wait 5s to allow disconnection of clients." )
193
+ }
194
+ time .Sleep (5 * time .Second )
195
+ stopWaitLoop = true
196
+ case s := <- signalChan :
197
+ fmt .Printf ("Got signal %s. Cancel test.\n " , s .String ())
198
+ cancelFunc ()
199
+ stopWaitLoop = true
189
200
}
190
201
}
191
202
@@ -217,17 +228,3 @@ func main() {
217
228
218
229
os .Exit (exitCode )
219
230
}
220
-
221
- func tearDownWorkers () {
222
- if ! tearDownInProgress {
223
- tearDownInProgress = true
224
-
225
- close (abortChan )
226
-
227
- delay , _ := time .ParseDuration (* argTearDownDelay )
228
- fmt .Printf ("Waiting %s for remaining workers\n " , delay )
229
- time .Sleep (delay )
230
-
231
- stopWaitLoop = true
232
- }
233
- }
0 commit comments