locust 2.29.2.dev34__py3-none-any.whl → 2.29.2.dev45__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. locust/_version.py +6 -2
  2. locust/contrib/fasthttp.py +1 -1
  3. locust/dispatch.py +7 -6
  4. locust/main.py +0 -1
  5. {locust-2.29.2.dev34.dist-info → locust-2.29.2.dev45.dist-info}/METADATA +31 -26
  6. locust-2.29.2.dev45.dist-info/RECORD +49 -0
  7. locust-2.29.2.dev45.dist-info/WHEEL +4 -0
  8. locust-2.29.2.dev45.dist-info/entry_points.txt +3 -0
  9. locust/test/__init__.py +0 -15
  10. locust/test/fake_module1_for_env_test.py +0 -7
  11. locust/test/fake_module2_for_env_test.py +0 -7
  12. locust/test/mock_locustfile.py +0 -56
  13. locust/test/mock_logging.py +0 -28
  14. locust/test/test_debugging.py +0 -39
  15. locust/test/test_dispatch.py +0 -4170
  16. locust/test/test_env.py +0 -283
  17. locust/test/test_fasthttp.py +0 -785
  18. locust/test/test_http.py +0 -325
  19. locust/test/test_interruptable_task.py +0 -48
  20. locust/test/test_load_locustfile.py +0 -228
  21. locust/test/test_locust_class.py +0 -831
  22. locust/test/test_log.py +0 -237
  23. locust/test/test_main.py +0 -2264
  24. locust/test/test_old_wait_api.py +0 -0
  25. locust/test/test_parser.py +0 -450
  26. locust/test/test_runners.py +0 -4476
  27. locust/test/test_sequential_taskset.py +0 -157
  28. locust/test/test_stats.py +0 -866
  29. locust/test/test_tags.py +0 -440
  30. locust/test/test_taskratio.py +0 -94
  31. locust/test/test_users.py +0 -69
  32. locust/test/test_util.py +0 -33
  33. locust/test/test_wait_time.py +0 -79
  34. locust/test/test_web.py +0 -1257
  35. locust/test/test_zmqrpc.py +0 -58
  36. locust/test/testcases.py +0 -248
  37. locust/test/util.py +0 -88
  38. locust-2.29.2.dev34.dist-info/RECORD +0 -79
  39. locust-2.29.2.dev34.dist-info/WHEEL +0 -5
  40. locust-2.29.2.dev34.dist-info/entry_points.txt +0 -2
  41. locust-2.29.2.dev34.dist-info/top_level.txt +0 -1
  42. {locust-2.29.2.dev34.dist-info → locust-2.29.2.dev45.dist-info}/LICENSE +0 -0
@@ -1,4170 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from locust import User
4
- from locust.dispatch import UsersDispatcher
5
- from locust.runners import WorkerNode
6
- from locust.test.util import clear_all_functools_lru_cache
7
-
8
- import math
9
- import time
10
- import unittest
11
- from operator import attrgetter
12
-
13
- _TOLERANCE = 0.025
14
-
15
-
16
- class TestRampUpUsersFromZero(unittest.TestCase):
17
- def test_ramp_up_users_to_3_workers_with_spawn_rate_of_0_5(self):
18
- """Final distribution should be {"User1": 3, "User2": 3, "User3": 3}"""
19
-
20
- class User1(User):
21
- weight = 1
22
-
23
- class User2(User):
24
- weight = 1
25
-
26
- class User3(User):
27
- weight = 1
28
-
29
- worker_node1 = WorkerNode("1")
30
- worker_node2 = WorkerNode("2")
31
- worker_node3 = WorkerNode("3")
32
-
33
- sleep_time = 0.2 # Speed-up test
34
-
35
- users_dispatcher = UsersDispatcher(
36
- worker_nodes=[worker_node1, worker_node2, worker_node3], user_classes=[User1, User2, User3]
37
- )
38
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=0.5)
39
- users_dispatcher._wait_between_dispatch = sleep_time
40
-
41
- ts = time.perf_counter()
42
- self.assertDictEqual(
43
- next(users_dispatcher),
44
- {
45
- "1": {"User1": 1, "User2": 0, "User3": 0},
46
- "2": {"User1": 0, "User2": 0, "User3": 0},
47
- "3": {"User1": 0, "User2": 0, "User3": 0},
48
- },
49
- )
50
- delta = time.perf_counter() - ts
51
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
52
-
53
- ts = time.perf_counter()
54
- self.assertDictEqual(
55
- next(users_dispatcher),
56
- {
57
- "1": {"User1": 1, "User2": 0, "User3": 0},
58
- "2": {"User1": 0, "User2": 1, "User3": 0},
59
- "3": {"User1": 0, "User2": 0, "User3": 0},
60
- },
61
- )
62
- delta = time.perf_counter() - ts
63
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
64
-
65
- ts = time.perf_counter()
66
- self.assertDictEqual(
67
- next(users_dispatcher),
68
- {
69
- "1": {"User1": 1, "User2": 0, "User3": 0},
70
- "2": {"User1": 0, "User2": 1, "User3": 0},
71
- "3": {"User1": 0, "User2": 0, "User3": 1},
72
- },
73
- )
74
- delta = time.perf_counter() - ts
75
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
76
-
77
- ts = time.perf_counter()
78
- self.assertDictEqual(
79
- next(users_dispatcher),
80
- {
81
- "1": {"User1": 2, "User2": 0, "User3": 0},
82
- "2": {"User1": 0, "User2": 1, "User3": 0},
83
- "3": {"User1": 0, "User2": 0, "User3": 1},
84
- },
85
- )
86
- delta = time.perf_counter() - ts
87
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
88
-
89
- ts = time.perf_counter()
90
- self.assertDictEqual(
91
- next(users_dispatcher),
92
- {
93
- "1": {"User1": 2, "User2": 0, "User3": 0},
94
- "2": {"User1": 0, "User2": 2, "User3": 0},
95
- "3": {"User1": 0, "User2": 0, "User3": 1},
96
- },
97
- )
98
- delta = time.perf_counter() - ts
99
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
100
-
101
- ts = time.perf_counter()
102
- self.assertDictEqual(
103
- next(users_dispatcher),
104
- {
105
- "1": {"User1": 2, "User2": 0, "User3": 0},
106
- "2": {"User1": 0, "User2": 2, "User3": 0},
107
- "3": {"User1": 0, "User2": 0, "User3": 2},
108
- },
109
- )
110
- delta = time.perf_counter() - ts
111
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
112
-
113
- ts = time.perf_counter()
114
- self.assertDictEqual(
115
- next(users_dispatcher),
116
- {
117
- "1": {"User1": 3, "User2": 0, "User3": 0},
118
- "2": {"User1": 0, "User2": 2, "User3": 0},
119
- "3": {"User1": 0, "User2": 0, "User3": 2},
120
- },
121
- )
122
- delta = time.perf_counter() - ts
123
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
124
-
125
- ts = time.perf_counter()
126
- self.assertDictEqual(
127
- next(users_dispatcher),
128
- {
129
- "1": {"User1": 3, "User2": 0, "User3": 0},
130
- "2": {"User1": 0, "User2": 3, "User3": 0},
131
- "3": {"User1": 0, "User2": 0, "User3": 2},
132
- },
133
- )
134
- delta = time.perf_counter() - ts
135
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
136
-
137
- ts = time.perf_counter()
138
- self.assertDictEqual(
139
- next(users_dispatcher),
140
- {
141
- "1": {"User1": 3, "User2": 0, "User3": 0},
142
- "2": {"User1": 0, "User2": 3, "User3": 0},
143
- "3": {"User1": 0, "User2": 0, "User3": 3},
144
- },
145
- )
146
- delta = time.perf_counter() - ts
147
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
148
-
149
- ts = time.perf_counter()
150
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
151
- delta = time.perf_counter() - ts
152
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
153
-
154
- def test_ramp_up_users_to_3_workers_with_spawn_rate_of_1(self):
155
- """Final distribution should be {"User1": 3, "User2": 3, "User3": 3}"""
156
-
157
- class User1(User):
158
- weight = 1
159
-
160
- class User2(User):
161
- weight = 1
162
-
163
- class User3(User):
164
- weight = 1
165
-
166
- worker_node1 = WorkerNode("1")
167
- worker_node2 = WorkerNode("2")
168
- worker_node3 = WorkerNode("3")
169
-
170
- sleep_time = 0.2 # Speed-up test
171
-
172
- users_dispatcher = UsersDispatcher(
173
- worker_nodes=[worker_node1, worker_node2, worker_node3], user_classes=[User1, User2, User3]
174
- )
175
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=1)
176
- users_dispatcher._wait_between_dispatch = sleep_time
177
-
178
- ts = time.perf_counter()
179
- self.assertDictEqual(
180
- next(users_dispatcher),
181
- {
182
- "1": {"User1": 1, "User2": 0, "User3": 0},
183
- "2": {"User1": 0, "User2": 0, "User3": 0},
184
- "3": {"User1": 0, "User2": 0, "User3": 0},
185
- },
186
- )
187
- delta = time.perf_counter() - ts
188
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
189
-
190
- ts = time.perf_counter()
191
- self.assertDictEqual(
192
- next(users_dispatcher),
193
- {
194
- "1": {"User1": 1, "User2": 0, "User3": 0},
195
- "2": {"User1": 0, "User2": 1, "User3": 0},
196
- "3": {"User1": 0, "User2": 0, "User3": 0},
197
- },
198
- )
199
- delta = time.perf_counter() - ts
200
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
201
-
202
- ts = time.perf_counter()
203
- self.assertDictEqual(
204
- next(users_dispatcher),
205
- {
206
- "1": {"User1": 1, "User2": 0, "User3": 0},
207
- "2": {"User1": 0, "User2": 1, "User3": 0},
208
- "3": {"User1": 0, "User2": 0, "User3": 1},
209
- },
210
- )
211
- delta = time.perf_counter() - ts
212
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
213
-
214
- ts = time.perf_counter()
215
- self.assertDictEqual(
216
- next(users_dispatcher),
217
- {
218
- "1": {"User1": 2, "User2": 0, "User3": 0},
219
- "2": {"User1": 0, "User2": 1, "User3": 0},
220
- "3": {"User1": 0, "User2": 0, "User3": 1},
221
- },
222
- )
223
- delta = time.perf_counter() - ts
224
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
225
-
226
- ts = time.perf_counter()
227
- self.assertDictEqual(
228
- next(users_dispatcher),
229
- {
230
- "1": {"User1": 2, "User2": 0, "User3": 0},
231
- "2": {"User1": 0, "User2": 2, "User3": 0},
232
- "3": {"User1": 0, "User2": 0, "User3": 1},
233
- },
234
- )
235
- delta = time.perf_counter() - ts
236
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
237
-
238
- ts = time.perf_counter()
239
- self.assertDictEqual(
240
- next(users_dispatcher),
241
- {
242
- "1": {"User1": 2, "User2": 0, "User3": 0},
243
- "2": {"User1": 0, "User2": 2, "User3": 0},
244
- "3": {"User1": 0, "User2": 0, "User3": 2},
245
- },
246
- )
247
- delta = time.perf_counter() - ts
248
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
249
-
250
- ts = time.perf_counter()
251
- self.assertDictEqual(
252
- next(users_dispatcher),
253
- {
254
- "1": {"User1": 3, "User2": 0, "User3": 0},
255
- "2": {"User1": 0, "User2": 2, "User3": 0},
256
- "3": {"User1": 0, "User2": 0, "User3": 2},
257
- },
258
- )
259
- delta = time.perf_counter() - ts
260
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
261
-
262
- ts = time.perf_counter()
263
- self.assertDictEqual(
264
- next(users_dispatcher),
265
- {
266
- "1": {"User1": 3, "User2": 0, "User3": 0},
267
- "2": {"User1": 0, "User2": 3, "User3": 0},
268
- "3": {"User1": 0, "User2": 0, "User3": 2},
269
- },
270
- )
271
- delta = time.perf_counter() - ts
272
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
273
-
274
- ts = time.perf_counter()
275
- self.assertDictEqual(
276
- next(users_dispatcher),
277
- {
278
- "1": {"User1": 3, "User2": 0, "User3": 0},
279
- "2": {"User1": 0, "User2": 3, "User3": 0},
280
- "3": {"User1": 0, "User2": 0, "User3": 3},
281
- },
282
- )
283
- delta = time.perf_counter() - ts
284
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
285
-
286
- ts = time.perf_counter()
287
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
288
- delta = time.perf_counter() - ts
289
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
290
-
291
- def test_ramp_up_users_to_4_workers_with_spawn_rate_of_1(self):
292
- """Final distribution should be {"User1": 3, "User2": 3, "User3": 3}"""
293
-
294
- class User1(User):
295
- weight = 1
296
-
297
- class User2(User):
298
- weight = 1
299
-
300
- class User3(User):
301
- weight = 1
302
-
303
- worker_node1 = WorkerNode("1")
304
- worker_node2 = WorkerNode("2")
305
- worker_node3 = WorkerNode("3")
306
- worker_node4 = WorkerNode("4")
307
-
308
- sleep_time = 0.2 # Speed-up test
309
-
310
- users_dispatcher = UsersDispatcher(
311
- worker_nodes=[worker_node1, worker_node2, worker_node3, worker_node4], user_classes=[User1, User2, User3]
312
- )
313
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=1)
314
- users_dispatcher._wait_between_dispatch = sleep_time
315
-
316
- ts = time.perf_counter()
317
- self.assertDictEqual(
318
- next(users_dispatcher),
319
- {
320
- "1": {"User1": 1, "User2": 0, "User3": 0},
321
- "2": {"User1": 0, "User2": 0, "User3": 0},
322
- "3": {"User1": 0, "User2": 0, "User3": 0},
323
- "4": {"User1": 0, "User2": 0, "User3": 0},
324
- },
325
- )
326
- delta = time.perf_counter() - ts
327
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
328
-
329
- ts = time.perf_counter()
330
- self.assertDictEqual(
331
- next(users_dispatcher),
332
- {
333
- "1": {"User1": 1, "User2": 0, "User3": 0},
334
- "2": {"User1": 0, "User2": 1, "User3": 0},
335
- "3": {"User1": 0, "User2": 0, "User3": 0},
336
- "4": {"User1": 0, "User2": 0, "User3": 0},
337
- },
338
- )
339
- delta = time.perf_counter() - ts
340
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
341
-
342
- ts = time.perf_counter()
343
- self.assertDictEqual(
344
- next(users_dispatcher),
345
- {
346
- "1": {"User1": 1, "User2": 0, "User3": 0},
347
- "2": {"User1": 0, "User2": 1, "User3": 0},
348
- "3": {"User1": 0, "User2": 0, "User3": 1},
349
- "4": {"User1": 0, "User2": 0, "User3": 0},
350
- },
351
- )
352
- delta = time.perf_counter() - ts
353
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
354
-
355
- ts = time.perf_counter()
356
- self.assertDictEqual(
357
- next(users_dispatcher),
358
- {
359
- "1": {"User1": 1, "User2": 0, "User3": 0},
360
- "2": {"User1": 0, "User2": 1, "User3": 0},
361
- "3": {"User1": 0, "User2": 0, "User3": 1},
362
- "4": {"User1": 1, "User2": 0, "User3": 0},
363
- },
364
- )
365
- delta = time.perf_counter() - ts
366
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
367
-
368
- ts = time.perf_counter()
369
- self.assertDictEqual(
370
- next(users_dispatcher),
371
- {
372
- "1": {"User1": 1, "User2": 1, "User3": 0},
373
- "2": {"User1": 0, "User2": 1, "User3": 0},
374
- "3": {"User1": 0, "User2": 0, "User3": 1},
375
- "4": {"User1": 1, "User2": 0, "User3": 0},
376
- },
377
- )
378
- delta = time.perf_counter() - ts
379
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
380
-
381
- ts = time.perf_counter()
382
- self.assertDictEqual(
383
- next(users_dispatcher),
384
- {
385
- "1": {"User1": 1, "User2": 1, "User3": 0},
386
- "2": {"User1": 0, "User2": 1, "User3": 1},
387
- "3": {"User1": 0, "User2": 0, "User3": 1},
388
- "4": {"User1": 1, "User2": 0, "User3": 0},
389
- },
390
- )
391
- delta = time.perf_counter() - ts
392
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
393
-
394
- ts = time.perf_counter()
395
- self.assertDictEqual(
396
- next(users_dispatcher),
397
- {
398
- "1": {"User1": 1, "User2": 1, "User3": 0},
399
- "2": {"User1": 0, "User2": 1, "User3": 1},
400
- "3": {"User1": 1, "User2": 0, "User3": 1},
401
- "4": {"User1": 1, "User2": 0, "User3": 0},
402
- },
403
- )
404
- delta = time.perf_counter() - ts
405
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
406
-
407
- ts = time.perf_counter()
408
- self.assertDictEqual(
409
- next(users_dispatcher),
410
- {
411
- "1": {"User1": 1, "User2": 1, "User3": 0},
412
- "2": {"User1": 0, "User2": 1, "User3": 1},
413
- "3": {"User1": 1, "User2": 0, "User3": 1},
414
- "4": {"User1": 1, "User2": 1, "User3": 0},
415
- },
416
- )
417
- delta = time.perf_counter() - ts
418
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
419
-
420
- ts = time.perf_counter()
421
- self.assertDictEqual(
422
- next(users_dispatcher),
423
- {
424
- "1": {"User1": 1, "User2": 1, "User3": 1},
425
- "2": {"User1": 0, "User2": 1, "User3": 1},
426
- "3": {"User1": 1, "User2": 0, "User3": 1},
427
- "4": {"User1": 1, "User2": 1, "User3": 0},
428
- },
429
- )
430
- delta = time.perf_counter() - ts
431
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
432
-
433
- ts = time.perf_counter()
434
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
435
- delta = time.perf_counter() - ts
436
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
437
-
438
- def test_ramp_up_users_to_3_workers_with_spawn_rate_of_2(self):
439
- """Final distribution should be {"User1": 3, "User2": 3, "User3": 3}"""
440
-
441
- class User1(User):
442
- weight = 1
443
-
444
- class User2(User):
445
- weight = 1
446
-
447
- class User3(User):
448
- weight = 1
449
-
450
- worker_node1 = WorkerNode("1")
451
- worker_node2 = WorkerNode("2")
452
- worker_node3 = WorkerNode("3")
453
-
454
- sleep_time = 0.2 # Speed-up test
455
-
456
- users_dispatcher = UsersDispatcher(
457
- worker_nodes=[worker_node1, worker_node2, worker_node3], user_classes=[User1, User2, User3]
458
- )
459
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=2)
460
- users_dispatcher._wait_between_dispatch = sleep_time
461
-
462
- ts = time.perf_counter()
463
- self.assertDictEqual(
464
- next(users_dispatcher),
465
- {
466
- "1": {"User1": 1, "User2": 0, "User3": 0},
467
- "2": {"User1": 0, "User2": 1, "User3": 0},
468
- "3": {"User1": 0, "User2": 0, "User3": 0},
469
- },
470
- )
471
- delta = time.perf_counter() - ts
472
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
473
-
474
- ts = time.perf_counter()
475
- self.assertDictEqual(
476
- next(users_dispatcher),
477
- {
478
- "1": {"User1": 2, "User2": 0, "User3": 0},
479
- "2": {"User1": 0, "User2": 1, "User3": 0},
480
- "3": {"User1": 0, "User2": 0, "User3": 1},
481
- },
482
- )
483
- delta = time.perf_counter() - ts
484
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
485
-
486
- ts = time.perf_counter()
487
- self.assertDictEqual(
488
- next(users_dispatcher),
489
- {
490
- "1": {"User1": 2, "User2": 0, "User3": 0},
491
- "2": {"User1": 0, "User2": 2, "User3": 0},
492
- "3": {"User1": 0, "User2": 0, "User3": 2},
493
- },
494
- )
495
- delta = time.perf_counter() - ts
496
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
497
-
498
- ts = time.perf_counter()
499
- self.assertDictEqual(
500
- next(users_dispatcher),
501
- {
502
- "1": {"User1": 3, "User2": 0, "User3": 0},
503
- "2": {"User1": 0, "User2": 3, "User3": 0},
504
- "3": {"User1": 0, "User2": 0, "User3": 2},
505
- },
506
- )
507
- delta = time.perf_counter() - ts
508
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
509
-
510
- ts = time.perf_counter()
511
- self.assertDictEqual(
512
- next(users_dispatcher),
513
- {
514
- "1": {"User1": 3, "User2": 0, "User3": 0},
515
- "2": {"User1": 0, "User2": 3, "User3": 0},
516
- "3": {"User1": 0, "User2": 0, "User3": 3},
517
- },
518
- )
519
- delta = time.perf_counter() - ts
520
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
521
-
522
- ts = time.perf_counter()
523
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
524
- delta = time.perf_counter() - ts
525
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
526
-
527
- def test_ramp_up_users_to_3_workers_with_spawn_rate_of_2_4(self):
528
- """Final distribution should be {"User1": 3, "User2": 3, "User3": 3}"""
529
-
530
- class User1(User):
531
- weight = 1
532
-
533
- class User2(User):
534
- weight = 1
535
-
536
- class User3(User):
537
- weight = 1
538
-
539
- worker_node1 = WorkerNode("1")
540
- worker_node2 = WorkerNode("2")
541
- worker_node3 = WorkerNode("3")
542
-
543
- sleep_time = 0.2 # Speed-up test
544
-
545
- users_dispatcher = UsersDispatcher(
546
- worker_nodes=[worker_node1, worker_node2, worker_node3], user_classes=[User1, User2, User3]
547
- )
548
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=2.4)
549
- users_dispatcher._wait_between_dispatch = sleep_time
550
-
551
- ts = time.perf_counter()
552
- self.assertDictEqual(
553
- next(users_dispatcher),
554
- {
555
- "1": {"User1": 1, "User2": 0, "User3": 0},
556
- "2": {"User1": 0, "User2": 1, "User3": 0},
557
- "3": {"User1": 0, "User2": 0, "User3": 0},
558
- },
559
- )
560
- delta = time.perf_counter() - ts
561
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
562
-
563
- ts = time.perf_counter()
564
- self.assertDictEqual(
565
- next(users_dispatcher),
566
- {
567
- "1": {"User1": 2, "User2": 0, "User3": 0},
568
- "2": {"User1": 0, "User2": 1, "User3": 0},
569
- "3": {"User1": 0, "User2": 0, "User3": 1},
570
- },
571
- )
572
- delta = time.perf_counter() - ts
573
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
574
-
575
- ts = time.perf_counter()
576
- self.assertDictEqual(
577
- next(users_dispatcher),
578
- {
579
- "1": {"User1": 2, "User2": 0, "User3": 0},
580
- "2": {"User1": 0, "User2": 2, "User3": 0},
581
- "3": {"User1": 0, "User2": 0, "User3": 2},
582
- },
583
- )
584
- delta = time.perf_counter() - ts
585
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
586
-
587
- ts = time.perf_counter()
588
- self.assertDictEqual(
589
- next(users_dispatcher),
590
- {
591
- "1": {"User1": 3, "User2": 0, "User3": 0},
592
- "2": {"User1": 0, "User2": 3, "User3": 0},
593
- "3": {"User1": 0, "User2": 0, "User3": 2},
594
- },
595
- )
596
- delta = time.perf_counter() - ts
597
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
598
-
599
- ts = time.perf_counter()
600
- self.assertDictEqual(
601
- next(users_dispatcher),
602
- {
603
- "1": {"User1": 3, "User2": 0, "User3": 0},
604
- "2": {"User1": 0, "User2": 3, "User3": 0},
605
- "3": {"User1": 0, "User2": 0, "User3": 3},
606
- },
607
- )
608
- delta = time.perf_counter() - ts
609
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
610
-
611
- ts = time.perf_counter()
612
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
613
- delta = time.perf_counter() - ts
614
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
615
-
616
- def test_ramp_up_users_to_3_workers_with_spawn_rate_of_3(self):
617
- """Final distribution should be {"User1": 3, "User2": 3, "User3": 3}"""
618
-
619
- class User1(User):
620
- weight = 1
621
-
622
- class User2(User):
623
- weight = 1
624
-
625
- class User3(User):
626
- weight = 1
627
-
628
- worker_node1 = WorkerNode("1")
629
- worker_node2 = WorkerNode("2")
630
- worker_node3 = WorkerNode("3")
631
-
632
- sleep_time = 0.2 # Speed-up test
633
-
634
- users_dispatcher = UsersDispatcher(
635
- worker_nodes=[worker_node1, worker_node2, worker_node3], user_classes=[User1, User2, User3]
636
- )
637
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=3)
638
- users_dispatcher._wait_between_dispatch = sleep_time
639
-
640
- ts = time.perf_counter()
641
- self.assertDictEqual(
642
- next(users_dispatcher),
643
- {
644
- "1": {"User1": 1, "User2": 0, "User3": 0},
645
- "2": {"User1": 0, "User2": 1, "User3": 0},
646
- "3": {"User1": 0, "User2": 0, "User3": 1},
647
- },
648
- )
649
- delta = time.perf_counter() - ts
650
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
651
-
652
- ts = time.perf_counter()
653
- self.assertDictEqual(
654
- next(users_dispatcher),
655
- {
656
- "1": {"User1": 2, "User2": 0, "User3": 0},
657
- "2": {"User1": 0, "User2": 2, "User3": 0},
658
- "3": {"User1": 0, "User2": 0, "User3": 2},
659
- },
660
- )
661
- delta = time.perf_counter() - ts
662
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
663
-
664
- ts = time.perf_counter()
665
- self.assertDictEqual(
666
- next(users_dispatcher),
667
- {
668
- "1": {"User1": 3, "User2": 0, "User3": 0},
669
- "2": {"User1": 0, "User2": 3, "User3": 0},
670
- "3": {"User1": 0, "User2": 0, "User3": 3},
671
- },
672
- )
673
- delta = time.perf_counter() - ts
674
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
675
-
676
- ts = time.perf_counter()
677
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
678
- delta = time.perf_counter() - ts
679
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
680
-
681
- def test_ramp_up_users_to_3_workers_with_spawn_rate_of_4(self):
682
- """Final distribution should be {"User1": 3, "User2": 3, "User3": 3}"""
683
-
684
- class User1(User):
685
- weight = 1
686
-
687
- class User2(User):
688
- weight = 1
689
-
690
- class User3(User):
691
- weight = 1
692
-
693
- worker_node1 = WorkerNode("1")
694
- worker_node2 = WorkerNode("2")
695
- worker_node3 = WorkerNode("3")
696
-
697
- sleep_time = 0.2 # Speed-up test
698
-
699
- users_dispatcher = UsersDispatcher(
700
- worker_nodes=[worker_node1, worker_node2, worker_node3], user_classes=[User1, User2, User3]
701
- )
702
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=4)
703
- users_dispatcher._wait_between_dispatch = sleep_time
704
-
705
- ts = time.perf_counter()
706
- self.assertDictEqual(
707
- next(users_dispatcher),
708
- {
709
- "1": {"User1": 2, "User2": 0, "User3": 0},
710
- "2": {"User1": 0, "User2": 1, "User3": 0},
711
- "3": {"User1": 0, "User2": 0, "User3": 1},
712
- },
713
- )
714
- delta = time.perf_counter() - ts
715
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
716
-
717
- ts = time.perf_counter()
718
- self.assertDictEqual(
719
- next(users_dispatcher),
720
- {
721
- "1": {"User1": 3, "User2": 0, "User3": 0},
722
- "2": {"User1": 0, "User2": 3, "User3": 0},
723
- "3": {"User1": 0, "User2": 0, "User3": 2},
724
- },
725
- )
726
- delta = time.perf_counter() - ts
727
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
728
-
729
- ts = time.perf_counter()
730
- self.assertDictEqual(
731
- next(users_dispatcher),
732
- {
733
- "1": {"User1": 3, "User2": 0, "User3": 0},
734
- "2": {"User1": 0, "User2": 3, "User3": 0},
735
- "3": {"User1": 0, "User2": 0, "User3": 3},
736
- },
737
- )
738
- delta = time.perf_counter() - ts
739
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
740
-
741
- ts = time.perf_counter()
742
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
743
- delta = time.perf_counter() - ts
744
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
745
-
746
- def test_ramp_up_users_to_3_workers_with_spawn_rate_of_9(self):
747
- """Final distribution should be {"User1": 3, "User2": 3, "User3": 3}"""
748
-
749
- class User1(User):
750
- weight = 1
751
-
752
- class User2(User):
753
- weight = 1
754
-
755
- class User3(User):
756
- weight = 1
757
-
758
- worker_node1 = WorkerNode("1")
759
- worker_node2 = WorkerNode("2")
760
- worker_node3 = WorkerNode("3")
761
-
762
- sleep_time = 0.2 # Speed-up test
763
-
764
- users_dispatcher = UsersDispatcher(
765
- worker_nodes=[worker_node1, worker_node2, worker_node3], user_classes=[User1, User2, User3]
766
- )
767
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=9)
768
- users_dispatcher._wait_between_dispatch = sleep_time
769
-
770
- ts = time.perf_counter()
771
- self.assertDictEqual(
772
- next(users_dispatcher),
773
- {
774
- "1": {"User1": 3, "User2": 0, "User3": 0},
775
- "2": {"User1": 0, "User2": 3, "User3": 0},
776
- "3": {"User1": 0, "User2": 0, "User3": 3},
777
- },
778
- )
779
- delta = time.perf_counter() - ts
780
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
781
-
782
- ts = time.perf_counter()
783
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
784
- delta = time.perf_counter() - ts
785
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
786
-
787
- def test_users_are_distributed_evenly_across_hosts(self):
788
- class User1(User):
789
- weight = 1
790
-
791
- class User2(User):
792
- weight = 1
793
-
794
- class User3(User):
795
- weight = 1
796
-
797
- worker_node1 = WorkerNode("hostname1_worker1")
798
- worker_node2 = WorkerNode("hostname1_worker2")
799
- worker_node3 = WorkerNode("hostname2_worker1")
800
- worker_node4 = WorkerNode("hostname2_worker2")
801
-
802
- sleep_time = 0.2 # Speed-up test
803
-
804
- users_dispatcher = UsersDispatcher(
805
- worker_nodes=[worker_node1, worker_node2, worker_node3, worker_node4], user_classes=[User1, User2, User3]
806
- )
807
- users_dispatcher.new_dispatch(target_user_count=6, spawn_rate=2)
808
- users_dispatcher._wait_between_dispatch = sleep_time
809
-
810
- ts = time.perf_counter()
811
- self.assertDictEqual(
812
- next(users_dispatcher),
813
- {
814
- "hostname1_worker1": {"User1": 1, "User2": 0, "User3": 0},
815
- "hostname1_worker2": {"User1": 0, "User2": 0, "User3": 0},
816
- "hostname2_worker1": {"User1": 0, "User2": 1, "User3": 0},
817
- "hostname2_worker2": {"User1": 0, "User2": 0, "User3": 0},
818
- },
819
- )
820
- delta = time.perf_counter() - ts
821
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
822
-
823
- ts = time.perf_counter()
824
- self.assertDictEqual(
825
- next(users_dispatcher),
826
- {
827
- "hostname1_worker1": {"User1": 1, "User2": 0, "User3": 0},
828
- "hostname1_worker2": {"User1": 0, "User2": 0, "User3": 1},
829
- "hostname2_worker1": {"User1": 0, "User2": 1, "User3": 0},
830
- "hostname2_worker2": {"User1": 1, "User2": 0, "User3": 0},
831
- },
832
- )
833
- delta = time.perf_counter() - ts
834
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
835
-
836
- ts = time.perf_counter()
837
- self.assertDictEqual(
838
- next(users_dispatcher),
839
- {
840
- "hostname1_worker1": {"User1": 1, "User2": 1, "User3": 0},
841
- "hostname1_worker2": {"User1": 0, "User2": 0, "User3": 1},
842
- "hostname2_worker1": {"User1": 0, "User2": 1, "User3": 1},
843
- "hostname2_worker2": {"User1": 1, "User2": 0, "User3": 0},
844
- },
845
- )
846
- ts = time.perf_counter()
847
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
848
- delta = time.perf_counter() - ts
849
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
850
-
851
- def test_implementation_of_dispatch_distribution_with_gcd(self):
852
- class User1(User):
853
- weight = 4
854
-
855
- class User2(User):
856
- weight = 5
857
-
858
- user_classes = [User1, User2]
859
- worker_node1 = WorkerNode("1")
860
-
861
- sleep_time = 0.2 # Speed-up test
862
-
863
- users_dispatcher = UsersDispatcher(worker_nodes=[worker_node1], user_classes=user_classes)
864
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=9)
865
-
866
- users_dispatcher._wait_between_dispatch = sleep_time
867
-
868
- ts = time.perf_counter()
869
- self.assertDictEqual(
870
- next(users_dispatcher),
871
- {
872
- "1": {"User1": 4, "User2": 5},
873
- },
874
- )
875
- delta = time.perf_counter() - ts
876
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
877
-
878
- ts = time.perf_counter()
879
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
880
- delta = time.perf_counter() - ts
881
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
882
-
883
- def test_implementation_of_dispatch_distribution_with_gcd_float_weight(self):
884
- class User1(User):
885
- weight = 0.8
886
-
887
- class User2(User):
888
- weight = 1
889
-
890
- normalized_weights_to_min_int = 5 # User1: 0.8 * 5 = 4; User2: 1 * 5 = 5
891
-
892
- user_classes = [User1, User2]
893
- worker_node1 = WorkerNode("1")
894
-
895
- sleep_time = 0.2 # Speed-up test
896
-
897
- users_dispatcher = UsersDispatcher(worker_nodes=[worker_node1], user_classes=user_classes)
898
- users_dispatcher.new_dispatch(target_user_count=18, spawn_rate=18)
899
-
900
- users_dispatcher._wait_between_dispatch = sleep_time
901
-
902
- ts = time.perf_counter()
903
- self.assertDictEqual(
904
- next(users_dispatcher),
905
- {
906
- "1": {
907
- "User1": int(normalized_weights_to_min_int * User1.weight * 2),
908
- "User2": int(normalized_weights_to_min_int * User2.weight * 2),
909
- },
910
- },
911
- )
912
- delta = time.perf_counter() - ts
913
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
914
-
915
- ts = time.perf_counter()
916
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
917
- delta = time.perf_counter() - ts
918
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
919
-
920
-
921
- class TestWaitBetweenDispatch(unittest.TestCase):
922
- def test_wait_between_dispatch(self):
923
- class User1(User):
924
- weight = 1
925
-
926
- user_classes = [User1]
927
-
928
- workers = [WorkerNode("1")]
929
-
930
- for spawn_rate, expected_wait_between_dispatch in [
931
- (0.5, 1 / 0.5),
932
- (1, 1),
933
- (2, 1),
934
- (2.4, 2 / 2.4),
935
- (4, 1),
936
- (9, 1),
937
- ]:
938
- users_dispatcher = UsersDispatcher(worker_nodes=workers, user_classes=user_classes)
939
- users_dispatcher.new_dispatch(target_user_count=0, spawn_rate=spawn_rate)
940
- self.assertEqual(users_dispatcher._wait_between_dispatch, expected_wait_between_dispatch)
941
-
942
-
943
- class TestRampDownUsersToZero(unittest.TestCase):
944
- def test_ramp_down_users_to_3_workers_with_spawn_rate_of_0_5(self):
945
- class User1(User):
946
- weight = 1
947
-
948
- class User2(User):
949
- weight = 1
950
-
951
- class User3(User):
952
- weight = 1
953
-
954
- user_classes = [User1, User2, User3]
955
-
956
- workers = [WorkerNode(str(i + 1)) for i in range(3)]
957
-
958
- initial_user_count = 9
959
-
960
- users_dispatcher = UsersDispatcher(worker_nodes=workers, user_classes=user_classes)
961
- users_dispatcher.new_dispatch(target_user_count=initial_user_count, spawn_rate=initial_user_count)
962
- users_dispatcher._wait_between_dispatch = 0
963
- list(users_dispatcher)
964
-
965
- sleep_time = 0.2 # Speed-up test
966
-
967
- users_dispatcher.new_dispatch(target_user_count=0, spawn_rate=0.5)
968
- users_dispatcher._wait_between_dispatch = sleep_time
969
-
970
- ts = time.perf_counter()
971
- self.assertDictEqual(
972
- next(users_dispatcher),
973
- {
974
- "1": {"User1": 3, "User2": 0, "User3": 0},
975
- "2": {"User1": 0, "User2": 3, "User3": 0},
976
- "3": {"User1": 0, "User2": 0, "User3": 2},
977
- },
978
- )
979
- delta = time.perf_counter() - ts
980
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
981
-
982
- ts = time.perf_counter()
983
- self.assertDictEqual(
984
- next(users_dispatcher),
985
- {
986
- "1": {"User1": 3, "User2": 0, "User3": 0},
987
- "2": {"User1": 0, "User2": 2, "User3": 0},
988
- "3": {"User1": 0, "User2": 0, "User3": 2},
989
- },
990
- )
991
- delta = time.perf_counter() - ts
992
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
993
-
994
- ts = time.perf_counter()
995
- self.assertDictEqual(
996
- next(users_dispatcher),
997
- {
998
- "1": {"User1": 2, "User2": 0, "User3": 0},
999
- "2": {"User1": 0, "User2": 2, "User3": 0},
1000
- "3": {"User1": 0, "User2": 0, "User3": 2},
1001
- },
1002
- )
1003
- delta = time.perf_counter() - ts
1004
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1005
-
1006
- ts = time.perf_counter()
1007
- self.assertDictEqual(
1008
- next(users_dispatcher),
1009
- {
1010
- "1": {"User1": 2, "User2": 0, "User3": 0},
1011
- "2": {"User1": 0, "User2": 2, "User3": 0},
1012
- "3": {"User1": 0, "User2": 0, "User3": 1},
1013
- },
1014
- )
1015
- delta = time.perf_counter() - ts
1016
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1017
-
1018
- ts = time.perf_counter()
1019
- self.assertDictEqual(
1020
- next(users_dispatcher),
1021
- {
1022
- "1": {"User1": 2, "User2": 0, "User3": 0},
1023
- "2": {"User1": 0, "User2": 1, "User3": 0},
1024
- "3": {"User1": 0, "User2": 0, "User3": 1},
1025
- },
1026
- )
1027
- delta = time.perf_counter() - ts
1028
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1029
-
1030
- ts = time.perf_counter()
1031
- self.assertDictEqual(
1032
- next(users_dispatcher),
1033
- {
1034
- "1": {"User1": 1, "User2": 0, "User3": 0},
1035
- "2": {"User1": 0, "User2": 1, "User3": 0},
1036
- "3": {"User1": 0, "User2": 0, "User3": 1},
1037
- },
1038
- )
1039
- delta = time.perf_counter() - ts
1040
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1041
-
1042
- ts = time.perf_counter()
1043
- self.assertDictEqual(
1044
- next(users_dispatcher),
1045
- {
1046
- "1": {"User1": 1, "User2": 0, "User3": 0},
1047
- "2": {"User1": 0, "User2": 1, "User3": 0},
1048
- "3": {"User1": 0, "User2": 0, "User3": 0},
1049
- },
1050
- )
1051
- delta = time.perf_counter() - ts
1052
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1053
-
1054
- ts = time.perf_counter()
1055
- self.assertDictEqual(
1056
- next(users_dispatcher),
1057
- {
1058
- "1": {"User1": 1, "User2": 0, "User3": 0},
1059
- "2": {"User1": 0, "User2": 0, "User3": 0},
1060
- "3": {"User1": 0, "User2": 0, "User3": 0},
1061
- },
1062
- )
1063
- delta = time.perf_counter() - ts
1064
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1065
-
1066
- ts = time.perf_counter()
1067
- self.assertDictEqual(
1068
- next(users_dispatcher),
1069
- {
1070
- "1": {"User1": 0, "User2": 0, "User3": 0},
1071
- "2": {"User1": 0, "User2": 0, "User3": 0},
1072
- "3": {"User1": 0, "User2": 0, "User3": 0},
1073
- },
1074
- )
1075
- delta = time.perf_counter() - ts
1076
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1077
-
1078
- ts = time.perf_counter()
1079
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
1080
- delta = time.perf_counter() - ts
1081
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
1082
-
1083
- # def test_ramp_down_users_on_workers_respecting_weight(self):
1084
- # class User1(User):
1085
- # weight = 1
1086
- #
1087
- # class User2(User):
1088
- # weight = 1
1089
- #
1090
- # class User3(User):
1091
- # weight = 1
1092
- #
1093
- # user_classes = [User1, User2, User3]
1094
- # workers = [WorkerNode(str(i + 1)) for i in range(3)]
1095
- #
1096
- # user_dispatcher = UsersDispatcher(worker_nodes= workers, user_classes = user_classes)
1097
- # user_dispatcher.new_dispatch(target_user_count=7, spawn_rate=7)
1098
- #
1099
- # dispatched_users = next(user_dispatcher)
1100
- # self.assertDictEqual(dispatched_users,
1101
- # {
1102
- # "1": {"User1": 3, "User2": 0, "User3": 0},
1103
- # "2": {"User1": 0, "User2": 2, "User3": 0},
1104
- # "3": {"User1": 0, "User2": 0, "User3": 2}
1105
- # })
1106
- #
1107
- # user_dispatcher.new_dispatch(target_user_count=16, spawn_rate=9)
1108
- # dispatched_users = next(user_dispatcher)
1109
- # self.assertDictEqual(dispatched_users,
1110
- # {
1111
- # "1": {"User1": 6, "User2": 0, "User3": 0},
1112
- # "2": {"User1": 0, "User2": 5, "User3": 0},
1113
- # "3": {"User1": 0, "User2": 0, "User3": 5}
1114
- # })
1115
- #
1116
- # user_dispatcher.new_dispatch(target_user_count=3, spawn_rate=15)
1117
- # dispatched_users = next(user_dispatcher)
1118
- # self.assertDictEqual(dispatched_users,
1119
- # {
1120
- # "1": {"User1": 1, "User2": 0, "User3": 0},
1121
- # "2": {"User1": 0, "User2": 1, "User3": 0},
1122
- # "3": {"User1": 0, "User2": 0, "User3": 1}
1123
- # })
1124
- #
1125
-
1126
- def test_ramp_down_users_to_3_workers_with_spawn_rate_of_1(self):
1127
- class User1(User):
1128
- weight = 1
1129
-
1130
- class User2(User):
1131
- weight = 1
1132
-
1133
- class User3(User):
1134
- weight = 1
1135
-
1136
- user_classes = [User1, User2, User3]
1137
-
1138
- workers = [WorkerNode(str(i + 1)) for i in range(3)]
1139
-
1140
- initial_user_count = 9
1141
-
1142
- users_dispatcher = UsersDispatcher(worker_nodes=workers, user_classes=user_classes)
1143
- users_dispatcher.new_dispatch(target_user_count=initial_user_count, spawn_rate=initial_user_count)
1144
- users_dispatcher._wait_between_dispatch = 0
1145
- list(users_dispatcher)
1146
-
1147
- sleep_time = 0.2 # Speed-up test
1148
-
1149
- users_dispatcher.new_dispatch(target_user_count=0, spawn_rate=1)
1150
- users_dispatcher._wait_between_dispatch = sleep_time
1151
-
1152
- ts = time.perf_counter()
1153
- self.assertDictEqual(
1154
- next(users_dispatcher),
1155
- {
1156
- "1": {"User1": 3, "User2": 0, "User3": 0},
1157
- "2": {"User1": 0, "User2": 3, "User3": 0},
1158
- "3": {"User1": 0, "User2": 0, "User3": 2},
1159
- },
1160
- )
1161
- delta = time.perf_counter() - ts
1162
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
1163
-
1164
- ts = time.perf_counter()
1165
- self.assertDictEqual(
1166
- next(users_dispatcher),
1167
- {
1168
- "1": {"User1": 3, "User2": 0, "User3": 0},
1169
- "2": {"User1": 0, "User2": 2, "User3": 0},
1170
- "3": {"User1": 0, "User2": 0, "User3": 2},
1171
- },
1172
- )
1173
- delta = time.perf_counter() - ts
1174
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1175
-
1176
- ts = time.perf_counter()
1177
- self.assertDictEqual(
1178
- next(users_dispatcher),
1179
- {
1180
- "1": {"User1": 2, "User2": 0, "User3": 0},
1181
- "2": {"User1": 0, "User2": 2, "User3": 0},
1182
- "3": {"User1": 0, "User2": 0, "User3": 2},
1183
- },
1184
- )
1185
- delta = time.perf_counter() - ts
1186
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1187
-
1188
- ts = time.perf_counter()
1189
- self.assertDictEqual(
1190
- next(users_dispatcher),
1191
- {
1192
- "1": {"User1": 2, "User2": 0, "User3": 0},
1193
- "2": {"User1": 0, "User2": 2, "User3": 0},
1194
- "3": {"User1": 0, "User2": 0, "User3": 1},
1195
- },
1196
- )
1197
- delta = time.perf_counter() - ts
1198
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1199
-
1200
- ts = time.perf_counter()
1201
- self.assertDictEqual(
1202
- next(users_dispatcher),
1203
- {
1204
- "1": {"User1": 2, "User2": 0, "User3": 0},
1205
- "2": {"User1": 0, "User2": 1, "User3": 0},
1206
- "3": {"User1": 0, "User2": 0, "User3": 1},
1207
- },
1208
- )
1209
- delta = time.perf_counter() - ts
1210
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1211
-
1212
- ts = time.perf_counter()
1213
- self.assertDictEqual(
1214
- next(users_dispatcher),
1215
- {
1216
- "1": {"User1": 1, "User2": 0, "User3": 0},
1217
- "2": {"User1": 0, "User2": 1, "User3": 0},
1218
- "3": {"User1": 0, "User2": 0, "User3": 1},
1219
- },
1220
- )
1221
- delta = time.perf_counter() - ts
1222
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1223
-
1224
- ts = time.perf_counter()
1225
- self.assertDictEqual(
1226
- next(users_dispatcher),
1227
- {
1228
- "1": {"User1": 1, "User2": 0, "User3": 0},
1229
- "2": {"User1": 0, "User2": 1, "User3": 0},
1230
- "3": {"User1": 0, "User2": 0, "User3": 0},
1231
- },
1232
- )
1233
- delta = time.perf_counter() - ts
1234
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1235
-
1236
- ts = time.perf_counter()
1237
- self.assertDictEqual(
1238
- next(users_dispatcher),
1239
- {
1240
- "1": {"User1": 1, "User2": 0, "User3": 0},
1241
- "2": {"User1": 0, "User2": 0, "User3": 0},
1242
- "3": {"User1": 0, "User2": 0, "User3": 0},
1243
- },
1244
- )
1245
- delta = time.perf_counter() - ts
1246
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1247
-
1248
- ts = time.perf_counter()
1249
- self.assertDictEqual(
1250
- next(users_dispatcher),
1251
- {
1252
- "1": {"User1": 0, "User2": 0, "User3": 0},
1253
- "2": {"User1": 0, "User2": 0, "User3": 0},
1254
- "3": {"User1": 0, "User2": 0, "User3": 0},
1255
- },
1256
- )
1257
- delta = time.perf_counter() - ts
1258
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1259
-
1260
- ts = time.perf_counter()
1261
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
1262
- delta = time.perf_counter() - ts
1263
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
1264
-
1265
- def test_ramp_down_users_to_4_workers_with_spawn_rate_of_1(self):
1266
- class User1(User):
1267
- weight = 1
1268
-
1269
- class User2(User):
1270
- weight = 1
1271
-
1272
- class User3(User):
1273
- weight = 1
1274
-
1275
- user_classes = [User1, User2, User3]
1276
-
1277
- workers = [WorkerNode(str(i + 1)) for i in range(4)]
1278
-
1279
- initial_user_count = 9
1280
-
1281
- users_dispatcher = UsersDispatcher(worker_nodes=workers, user_classes=user_classes)
1282
- users_dispatcher.new_dispatch(target_user_count=initial_user_count, spawn_rate=initial_user_count)
1283
- users_dispatcher._wait_between_dispatch = 0
1284
- list(users_dispatcher)
1285
-
1286
- sleep_time = 0.2 # Speed-up test
1287
-
1288
- users_dispatcher.new_dispatch(target_user_count=0, spawn_rate=1)
1289
- users_dispatcher._wait_between_dispatch = sleep_time
1290
-
1291
- ts = time.perf_counter()
1292
- self.assertDictEqual(
1293
- next(users_dispatcher),
1294
- {
1295
- "1": {"User1": 1, "User2": 1, "User3": 0},
1296
- "2": {"User1": 0, "User2": 1, "User3": 1},
1297
- "3": {"User1": 1, "User2": 0, "User3": 1},
1298
- "4": {"User1": 1, "User2": 1, "User3": 0},
1299
- },
1300
- )
1301
- delta = time.perf_counter() - ts
1302
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
1303
-
1304
- ts = time.perf_counter()
1305
- self.assertDictEqual(
1306
- next(users_dispatcher),
1307
- {
1308
- "1": {"User1": 1, "User2": 1, "User3": 0},
1309
- "2": {"User1": 0, "User2": 1, "User3": 1},
1310
- "3": {"User1": 1, "User2": 0, "User3": 1},
1311
- "4": {"User1": 1, "User2": 0, "User3": 0},
1312
- },
1313
- )
1314
- delta = time.perf_counter() - ts
1315
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1316
-
1317
- ts = time.perf_counter()
1318
- self.assertDictEqual(
1319
- next(users_dispatcher),
1320
- {
1321
- "1": {"User1": 1, "User2": 1, "User3": 0},
1322
- "2": {"User1": 0, "User2": 1, "User3": 1},
1323
- "3": {"User1": 0, "User2": 0, "User3": 1},
1324
- "4": {"User1": 1, "User2": 0, "User3": 0},
1325
- },
1326
- )
1327
- delta = time.perf_counter() - ts
1328
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1329
-
1330
- ts = time.perf_counter()
1331
- self.assertDictEqual(
1332
- next(users_dispatcher),
1333
- {
1334
- "1": {"User1": 1, "User2": 1, "User3": 0},
1335
- "2": {"User1": 0, "User2": 1, "User3": 0},
1336
- "3": {"User1": 0, "User2": 0, "User3": 1},
1337
- "4": {"User1": 1, "User2": 0, "User3": 0},
1338
- },
1339
- )
1340
- delta = time.perf_counter() - ts
1341
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1342
-
1343
- ts = time.perf_counter()
1344
- self.assertDictEqual(
1345
- next(users_dispatcher),
1346
- {
1347
- "1": {"User1": 1, "User2": 0, "User3": 0},
1348
- "2": {"User1": 0, "User2": 1, "User3": 0},
1349
- "3": {"User1": 0, "User2": 0, "User3": 1},
1350
- "4": {"User1": 1, "User2": 0, "User3": 0},
1351
- },
1352
- )
1353
- delta = time.perf_counter() - ts
1354
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1355
-
1356
- ts = time.perf_counter()
1357
- self.assertDictEqual(
1358
- next(users_dispatcher),
1359
- {
1360
- "1": {"User1": 1, "User2": 0, "User3": 0},
1361
- "2": {"User1": 0, "User2": 1, "User3": 0},
1362
- "3": {"User1": 0, "User2": 0, "User3": 1},
1363
- "4": {"User1": 0, "User2": 0, "User3": 0},
1364
- },
1365
- )
1366
- delta = time.perf_counter() - ts
1367
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1368
-
1369
- ts = time.perf_counter()
1370
- self.assertDictEqual(
1371
- next(users_dispatcher),
1372
- {
1373
- "1": {"User1": 1, "User2": 0, "User3": 0},
1374
- "2": {"User1": 0, "User2": 1, "User3": 0},
1375
- "3": {"User1": 0, "User2": 0, "User3": 0},
1376
- "4": {"User1": 0, "User2": 0, "User3": 0},
1377
- },
1378
- )
1379
- delta = time.perf_counter() - ts
1380
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1381
-
1382
- ts = time.perf_counter()
1383
- self.assertDictEqual(
1384
- next(users_dispatcher),
1385
- {
1386
- "1": {"User1": 1, "User2": 0, "User3": 0},
1387
- "2": {"User1": 0, "User2": 0, "User3": 0},
1388
- "3": {"User1": 0, "User2": 0, "User3": 0},
1389
- "4": {"User1": 0, "User2": 0, "User3": 0},
1390
- },
1391
- )
1392
- delta = time.perf_counter() - ts
1393
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1394
-
1395
- ts = time.perf_counter()
1396
- self.assertDictEqual(
1397
- next(users_dispatcher),
1398
- {
1399
- "1": {"User1": 0, "User2": 0, "User3": 0},
1400
- "2": {"User1": 0, "User2": 0, "User3": 0},
1401
- "3": {"User1": 0, "User2": 0, "User3": 0},
1402
- "4": {"User1": 0, "User2": 0, "User3": 0},
1403
- },
1404
- )
1405
- delta = time.perf_counter() - ts
1406
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1407
-
1408
- ts = time.perf_counter()
1409
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
1410
- delta = time.perf_counter() - ts
1411
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
1412
-
1413
- def test_ramp_down_users_to_3_workers_with_spawn_rate_of_2(self):
1414
- class User1(User):
1415
- weight = 1
1416
-
1417
- class User2(User):
1418
- weight = 1
1419
-
1420
- class User3(User):
1421
- weight = 1
1422
-
1423
- user_classes = [User1, User2, User3]
1424
-
1425
- workers = [WorkerNode(str(i + 1)) for i in range(3)]
1426
-
1427
- initial_user_count = 9
1428
-
1429
- users_dispatcher = UsersDispatcher(worker_nodes=workers, user_classes=user_classes)
1430
- users_dispatcher.new_dispatch(target_user_count=initial_user_count, spawn_rate=initial_user_count)
1431
- users_dispatcher._wait_between_dispatch = 0
1432
- list(users_dispatcher)
1433
-
1434
- sleep_time = 0.2 # Speed-up test
1435
-
1436
- users_dispatcher.new_dispatch(target_user_count=0, spawn_rate=2)
1437
- users_dispatcher._wait_between_dispatch = sleep_time
1438
-
1439
- ts = time.perf_counter()
1440
- self.assertDictEqual(
1441
- next(users_dispatcher),
1442
- {
1443
- "1": {"User1": 3, "User2": 0, "User3": 0},
1444
- "2": {"User1": 0, "User2": 2, "User3": 0},
1445
- "3": {"User1": 0, "User2": 0, "User3": 2},
1446
- },
1447
- )
1448
- delta = time.perf_counter() - ts
1449
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
1450
-
1451
- ts = time.perf_counter()
1452
- self.assertDictEqual(
1453
- next(users_dispatcher),
1454
- {
1455
- "1": {"User1": 2, "User2": 0, "User3": 0},
1456
- "2": {"User1": 0, "User2": 2, "User3": 0},
1457
- "3": {"User1": 0, "User2": 0, "User3": 1},
1458
- },
1459
- )
1460
- delta = time.perf_counter() - ts
1461
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1462
-
1463
- ts = time.perf_counter()
1464
- self.assertDictEqual(
1465
- next(users_dispatcher),
1466
- {
1467
- "1": {"User1": 1, "User2": 0, "User3": 0},
1468
- "2": {"User1": 0, "User2": 1, "User3": 0},
1469
- "3": {"User1": 0, "User2": 0, "User3": 1},
1470
- },
1471
- )
1472
- delta = time.perf_counter() - ts
1473
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1474
-
1475
- ts = time.perf_counter()
1476
- self.assertDictEqual(
1477
- next(users_dispatcher),
1478
- {
1479
- "1": {"User1": 1, "User2": 0, "User3": 0},
1480
- "2": {"User1": 0, "User2": 0, "User3": 0},
1481
- "3": {"User1": 0, "User2": 0, "User3": 0},
1482
- },
1483
- )
1484
- delta = time.perf_counter() - ts
1485
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1486
-
1487
- ts = time.perf_counter()
1488
- self.assertDictEqual(
1489
- next(users_dispatcher),
1490
- {
1491
- "1": {"User1": 0, "User2": 0, "User3": 0},
1492
- "2": {"User1": 0, "User2": 0, "User3": 0},
1493
- "3": {"User1": 0, "User2": 0, "User3": 0},
1494
- },
1495
- )
1496
- delta = time.perf_counter() - ts
1497
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1498
-
1499
- ts = time.perf_counter()
1500
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
1501
- delta = time.perf_counter() - ts
1502
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
1503
-
1504
- def test_ramp_down_users_to_3_workers_with_spawn_rate_of_2_4(self):
1505
- class User1(User):
1506
- weight = 1
1507
-
1508
- class User2(User):
1509
- weight = 1
1510
-
1511
- class User3(User):
1512
- weight = 1
1513
-
1514
- user_classes = [User1, User2, User3]
1515
-
1516
- workers = [WorkerNode(str(i + 1)) for i in range(3)]
1517
-
1518
- initial_user_count = 9
1519
-
1520
- users_dispatcher = UsersDispatcher(worker_nodes=workers, user_classes=user_classes)
1521
- users_dispatcher.new_dispatch(target_user_count=initial_user_count, spawn_rate=initial_user_count)
1522
- users_dispatcher._wait_between_dispatch = 0
1523
- list(users_dispatcher)
1524
-
1525
- sleep_time = 0.2 # Speed-up test
1526
-
1527
- users_dispatcher.new_dispatch(target_user_count=0, spawn_rate=2.4)
1528
- users_dispatcher._wait_between_dispatch = sleep_time
1529
-
1530
- ts = time.perf_counter()
1531
- self.assertDictEqual(
1532
- next(users_dispatcher),
1533
- {
1534
- "1": {"User1": 3, "User2": 0, "User3": 0},
1535
- "2": {"User1": 0, "User2": 2, "User3": 0},
1536
- "3": {"User1": 0, "User2": 0, "User3": 2},
1537
- },
1538
- )
1539
- delta = time.perf_counter() - ts
1540
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
1541
-
1542
- ts = time.perf_counter()
1543
- self.assertDictEqual(
1544
- next(users_dispatcher),
1545
- {
1546
- "1": {"User1": 2, "User2": 0, "User3": 0},
1547
- "2": {"User1": 0, "User2": 2, "User3": 0},
1548
- "3": {"User1": 0, "User2": 0, "User3": 1},
1549
- },
1550
- )
1551
- delta = time.perf_counter() - ts
1552
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1553
-
1554
- ts = time.perf_counter()
1555
- self.assertDictEqual(
1556
- next(users_dispatcher),
1557
- {
1558
- "1": {"User1": 1, "User2": 0, "User3": 0},
1559
- "2": {"User1": 0, "User2": 1, "User3": 0},
1560
- "3": {"User1": 0, "User2": 0, "User3": 1},
1561
- },
1562
- )
1563
- delta = time.perf_counter() - ts
1564
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1565
-
1566
- ts = time.perf_counter()
1567
- self.assertDictEqual(
1568
- next(users_dispatcher),
1569
- {
1570
- "1": {"User1": 1, "User2": 0, "User3": 0},
1571
- "2": {"User1": 0, "User2": 0, "User3": 0},
1572
- "3": {"User1": 0, "User2": 0, "User3": 0},
1573
- },
1574
- )
1575
- delta = time.perf_counter() - ts
1576
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1577
-
1578
- ts = time.perf_counter()
1579
- self.assertDictEqual(
1580
- next(users_dispatcher),
1581
- {
1582
- "1": {"User1": 0, "User2": 0, "User3": 0},
1583
- "2": {"User1": 0, "User2": 0, "User3": 0},
1584
- "3": {"User1": 0, "User2": 0, "User3": 0},
1585
- },
1586
- )
1587
- delta = time.perf_counter() - ts
1588
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1589
-
1590
- ts = time.perf_counter()
1591
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
1592
- delta = time.perf_counter() - ts
1593
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
1594
-
1595
- def test_ramp_down_users_to_3_workers_with_spawn_rate_of_3(self):
1596
- class User1(User):
1597
- weight = 1
1598
-
1599
- class User2(User):
1600
- weight = 1
1601
-
1602
- class User3(User):
1603
- weight = 1
1604
-
1605
- user_classes = [User1, User2, User3]
1606
-
1607
- workers = [WorkerNode(str(i + 1)) for i in range(3)]
1608
-
1609
- initial_user_count = 9
1610
-
1611
- users_dispatcher = UsersDispatcher(worker_nodes=workers, user_classes=user_classes)
1612
- users_dispatcher.new_dispatch(target_user_count=initial_user_count, spawn_rate=initial_user_count)
1613
- users_dispatcher._wait_between_dispatch = 0
1614
- list(users_dispatcher)
1615
-
1616
- sleep_time = 0.2 # Speed-up test
1617
-
1618
- users_dispatcher.new_dispatch(target_user_count=0, spawn_rate=3)
1619
- users_dispatcher._wait_between_dispatch = sleep_time
1620
-
1621
- ts = time.perf_counter()
1622
- self.assertDictEqual(
1623
- next(users_dispatcher),
1624
- {
1625
- "1": {"User1": 2, "User2": 0, "User3": 0},
1626
- "2": {"User1": 0, "User2": 2, "User3": 0},
1627
- "3": {"User1": 0, "User2": 0, "User3": 2},
1628
- },
1629
- )
1630
- delta = time.perf_counter() - ts
1631
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
1632
-
1633
- ts = time.perf_counter()
1634
- self.assertDictEqual(
1635
- next(users_dispatcher),
1636
- {
1637
- "1": {"User1": 1, "User2": 0, "User3": 0},
1638
- "2": {"User1": 0, "User2": 1, "User3": 0},
1639
- "3": {"User1": 0, "User2": 0, "User3": 1},
1640
- },
1641
- )
1642
- delta = time.perf_counter() - ts
1643
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1644
-
1645
- ts = time.perf_counter()
1646
- self.assertDictEqual(
1647
- next(users_dispatcher),
1648
- {
1649
- "1": {"User1": 0, "User2": 0, "User3": 0},
1650
- "2": {"User1": 0, "User2": 0, "User3": 0},
1651
- "3": {"User1": 0, "User2": 0, "User3": 0},
1652
- },
1653
- )
1654
- delta = time.perf_counter() - ts
1655
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1656
-
1657
- ts = time.perf_counter()
1658
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
1659
- delta = time.perf_counter() - ts
1660
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
1661
-
1662
- def test_ramp_down_users_to_3_workers_with_spawn_rate_of_4(self):
1663
- class User1(User):
1664
- weight = 1
1665
-
1666
- class User2(User):
1667
- weight = 1
1668
-
1669
- class User3(User):
1670
- weight = 1
1671
-
1672
- user_classes = [User1, User2, User3]
1673
-
1674
- workers = [WorkerNode(str(i + 1)) for i in range(3)]
1675
-
1676
- initial_user_count = 9
1677
-
1678
- users_dispatcher = UsersDispatcher(worker_nodes=workers, user_classes=user_classes)
1679
- users_dispatcher.new_dispatch(target_user_count=initial_user_count, spawn_rate=initial_user_count)
1680
- users_dispatcher._wait_between_dispatch = 0
1681
- list(users_dispatcher)
1682
-
1683
- sleep_time = 0.2 # Speed-up test
1684
-
1685
- users_dispatcher.new_dispatch(target_user_count=0, spawn_rate=4)
1686
- users_dispatcher._wait_between_dispatch = sleep_time
1687
-
1688
- ts = time.perf_counter()
1689
- self.assertDictEqual(
1690
- next(users_dispatcher),
1691
- {
1692
- "1": {"User1": 2, "User2": 0, "User3": 0},
1693
- "2": {"User1": 0, "User2": 2, "User3": 0},
1694
- "3": {"User1": 0, "User2": 0, "User3": 1},
1695
- },
1696
- )
1697
- delta = time.perf_counter() - ts
1698
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
1699
-
1700
- ts = time.perf_counter()
1701
- self.assertDictEqual(
1702
- next(users_dispatcher),
1703
- {
1704
- "1": {"User1": 1, "User2": 0, "User3": 0},
1705
- "2": {"User1": 0, "User2": 0, "User3": 0},
1706
- "3": {"User1": 0, "User2": 0, "User3": 0},
1707
- },
1708
- )
1709
- delta = time.perf_counter() - ts
1710
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1711
-
1712
- ts = time.perf_counter()
1713
- self.assertDictEqual(
1714
- next(users_dispatcher),
1715
- {
1716
- "1": {"User1": 0, "User2": 0, "User3": 0},
1717
- "2": {"User1": 0, "User2": 0, "User3": 0},
1718
- "3": {"User1": 0, "User2": 0, "User3": 0},
1719
- },
1720
- )
1721
- delta = time.perf_counter() - ts
1722
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
1723
-
1724
- ts = time.perf_counter()
1725
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
1726
- delta = time.perf_counter() - ts
1727
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
1728
-
1729
- def test_ramp_down_users_to_3_workers_with_spawn_rate_of_9(self):
1730
- class User1(User):
1731
- weight = 1
1732
-
1733
- class User2(User):
1734
- weight = 1
1735
-
1736
- class User3(User):
1737
- weight = 1
1738
-
1739
- user_classes = [User1, User2, User3]
1740
-
1741
- workers = [WorkerNode(str(i + 1)) for i in range(3)]
1742
-
1743
- initial_user_count = 9
1744
-
1745
- users_dispatcher = UsersDispatcher(worker_nodes=workers, user_classes=user_classes)
1746
- users_dispatcher.new_dispatch(target_user_count=initial_user_count, spawn_rate=initial_user_count)
1747
- users_dispatcher._wait_between_dispatch = 0
1748
- list(users_dispatcher)
1749
-
1750
- sleep_time = 0.2 # Speed-up test
1751
-
1752
- users_dispatcher.new_dispatch(target_user_count=0, spawn_rate=9)
1753
- users_dispatcher._wait_between_dispatch = sleep_time
1754
-
1755
- ts = time.perf_counter()
1756
- self.assertDictEqual(
1757
- next(users_dispatcher),
1758
- {
1759
- "1": {"User1": 0, "User2": 0, "User3": 0},
1760
- "2": {"User1": 0, "User2": 0, "User3": 0},
1761
- "3": {"User1": 0, "User2": 0, "User3": 0},
1762
- },
1763
- )
1764
- delta = time.perf_counter() - ts
1765
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
1766
-
1767
- ts = time.perf_counter()
1768
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
1769
- delta = time.perf_counter() - ts
1770
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
1771
-
1772
-
1773
- @unittest.skip(reason="takes too long. run this manually if you change dispatch logic.")
1774
- class TestRampUpThenDownThenUp(unittest.TestCase):
1775
- def test_ramp_up_then_down_then_up(self):
1776
- for user1_weight, user2_weight, user3_weight, user4_weight, user5_weight in [
1777
- (1, 1, 1, 1, 1),
1778
- (1, 2, 3, 4, 5),
1779
- (1, 3, 5, 7, 9),
1780
- ]:
1781
-
1782
- class User1(User):
1783
- weight = user1_weight
1784
-
1785
- class User2(User):
1786
- weight = user2_weight
1787
-
1788
- class User3(User):
1789
- weight = user3_weight
1790
-
1791
- class User4(User):
1792
- weight = user4_weight
1793
-
1794
- class User5(User):
1795
- weight = user5_weight
1796
-
1797
- all_user_classes = [User1, User2, User3, User4, User5]
1798
-
1799
- for number_of_user_classes in range(1, len(all_user_classes) + 1):
1800
- user_classes = all_user_classes[:number_of_user_classes]
1801
-
1802
- for max_user_count, min_user_count in [(30, 15), (54, 21), (14165, 1476)]:
1803
- for worker_count in [1, 3, 5, 9]:
1804
- workers = [WorkerNode(str(i + 1)) for i in range(worker_count)]
1805
-
1806
- users_dispatcher = UsersDispatcher(worker_nodes=workers, user_classes=user_classes)
1807
-
1808
- # Ramp-up to go to `min_user_count` #########
1809
-
1810
- users_dispatcher.new_dispatch(target_user_count=min_user_count, spawn_rate=1)
1811
- users_dispatcher._wait_between_dispatch = 0
1812
-
1813
- all_dispatched_users_ramp_up_to_min_user_count = list(users_dispatcher)
1814
-
1815
- # Ramp-up to go to `max_user_count` #########
1816
-
1817
- users_dispatcher.new_dispatch(target_user_count=max_user_count, spawn_rate=1)
1818
- users_dispatcher._wait_between_dispatch = 0
1819
-
1820
- list(users_dispatcher)
1821
-
1822
- # Ramp-down go back to `min_user_count` #########
1823
-
1824
- users_dispatcher.new_dispatch(target_user_count=min_user_count, spawn_rate=1)
1825
- users_dispatcher._wait_between_dispatch = 0
1826
-
1827
- all_dispatched_users_ramp_down_to_min_user_count = list(users_dispatcher)
1828
-
1829
- # Assertions #########
1830
-
1831
- self.assertDictEqual(
1832
- all_dispatched_users_ramp_up_to_min_user_count[-1],
1833
- all_dispatched_users_ramp_down_to_min_user_count[-1],
1834
- )
1835
-
1836
-
1837
- class TestDispatchUsersToWorkersHavingTheSameUsersAsTheTarget(unittest.TestCase):
1838
- def test_dispatch_users_to_3_workers(self):
1839
- """Final distribution should be {"User1": 3, "User2": 3, "User3": 3}"""
1840
-
1841
- class User1(User):
1842
- weight = 1
1843
-
1844
- class User2(User):
1845
- weight = 1
1846
-
1847
- class User3(User):
1848
- weight = 1
1849
-
1850
- user_classes = [User1, User2, User3]
1851
-
1852
- user_count = 9
1853
-
1854
- for spawn_rate in [0.15, 0.5, 1, 2, 2.4, 3, 4, 9]:
1855
- workers = [WorkerNode(str(i + 1)) for i in range(3)]
1856
-
1857
- users_dispatcher = UsersDispatcher(worker_nodes=workers, user_classes=user_classes)
1858
- users_dispatcher.new_dispatch(target_user_count=user_count, spawn_rate=user_count)
1859
- users_dispatcher._wait_between_dispatch = 0
1860
- list(users_dispatcher)
1861
-
1862
- sleep_time = 0.2 # Speed-up test
1863
-
1864
- users_dispatcher.new_dispatch(target_user_count=user_count, spawn_rate=spawn_rate)
1865
- users_dispatcher._wait_between_dispatch = sleep_time
1866
-
1867
- ts = time.perf_counter()
1868
- self.assertDictEqual(
1869
- next(users_dispatcher),
1870
- {
1871
- "1": {"User1": 3, "User2": 0, "User3": 0},
1872
- "2": {"User1": 0, "User2": 3, "User3": 0},
1873
- "3": {"User1": 0, "User2": 0, "User3": 3},
1874
- },
1875
- )
1876
- delta = time.perf_counter() - ts
1877
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
1878
-
1879
- ts = time.perf_counter()
1880
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
1881
- delta = time.perf_counter() - ts
1882
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
1883
-
1884
- clear_all_functools_lru_cache()
1885
-
1886
-
1887
- class TestDistributionIsRespectedDuringDispatch(unittest.TestCase):
1888
- def test_dispatch_75_users_to_4_workers_with_spawn_rate_of_5(self):
1889
- """
1890
- Test case covering reported issue in https://github.com/locustio/locust/pull/1621#issuecomment-853624275.
1891
-
1892
- The case is to ramp-up from 0 to 75 users with two user classes. `User1` has a weight of 1 and `User2`
1893
- has a weight of 2. The original issue was with 500 users, but to keep the test shorter, we use 75 users.
1894
-
1895
- Final distribution should be {"User1": 25, "User2": 50}
1896
- """
1897
-
1898
- class User1(User):
1899
- weight = 1
1900
-
1901
- class User2(User):
1902
- weight = 2
1903
-
1904
- worker_node1 = WorkerNode("1")
1905
- worker_node2 = WorkerNode("2")
1906
- worker_node3 = WorkerNode("3")
1907
- worker_node4 = WorkerNode("4")
1908
-
1909
- users_dispatcher = UsersDispatcher(
1910
- worker_nodes=[worker_node1, worker_node2, worker_node3, worker_node4], user_classes=[User1, User2]
1911
- )
1912
- users_dispatcher.new_dispatch(target_user_count=75, spawn_rate=5)
1913
- users_dispatcher._wait_between_dispatch = 0
1914
-
1915
- # total user count = 5
1916
- dispatched_users = next(users_dispatcher)
1917
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 3})
1918
- self.assertDictEqual(
1919
- dispatched_users,
1920
- {
1921
- "1": {"User1": 1, "User2": 1},
1922
- "2": {"User1": 1, "User2": 0},
1923
- "3": {"User1": 0, "User2": 1},
1924
- "4": {"User1": 0, "User2": 1},
1925
- },
1926
- )
1927
-
1928
- # total user count = 10
1929
- dispatched_users = next(users_dispatcher)
1930
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 3, "User2": 7})
1931
- self.assertDictEqual(
1932
- dispatched_users,
1933
- {
1934
- "1": {"User1": 1, "User2": 2},
1935
- "2": {"User1": 1, "User2": 2},
1936
- "3": {"User1": 0, "User2": 2},
1937
- "4": {"User1": 1, "User2": 1},
1938
- },
1939
- )
1940
-
1941
- # total user count = 15
1942
- dispatched_users = next(users_dispatcher)
1943
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 5, "User2": 10})
1944
- self.assertDictEqual(
1945
- dispatched_users,
1946
- {
1947
- "1": {"User1": 1, "User2": 3},
1948
- "2": {"User1": 2, "User2": 2},
1949
- "3": {"User1": 1, "User2": 3},
1950
- "4": {"User1": 1, "User2": 2},
1951
- },
1952
- )
1953
-
1954
- # total user count = 20
1955
- dispatched_users = next(users_dispatcher)
1956
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 7, "User2": 13})
1957
- self.assertDictEqual(
1958
- dispatched_users,
1959
- {
1960
- "1": {"User1": 2, "User2": 3},
1961
- "2": {"User1": 2, "User2": 3},
1962
- "3": {"User1": 1, "User2": 4},
1963
- "4": {"User1": 2, "User2": 3},
1964
- },
1965
- )
1966
-
1967
- # total user count = 25
1968
- dispatched_users = next(users_dispatcher)
1969
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 8, "User2": 17})
1970
- self.assertDictEqual(
1971
- dispatched_users,
1972
- {
1973
- "1": {"User1": 2, "User2": 5},
1974
- "2": {"User1": 2, "User2": 4},
1975
- "3": {"User1": 2, "User2": 4},
1976
- "4": {"User1": 2, "User2": 4},
1977
- },
1978
- )
1979
-
1980
- # total user count = 30
1981
- dispatched_users = next(users_dispatcher)
1982
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 10, "User2": 20})
1983
- self.assertDictEqual(
1984
- dispatched_users,
1985
- {
1986
- "1": {"User1": 3, "User2": 5},
1987
- "2": {"User1": 3, "User2": 5},
1988
- "3": {"User1": 2, "User2": 5},
1989
- "4": {"User1": 2, "User2": 5},
1990
- },
1991
- )
1992
-
1993
- # total user count = 35
1994
- dispatched_users = next(users_dispatcher)
1995
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 12, "User2": 23})
1996
- self.assertDictEqual(
1997
- dispatched_users,
1998
- {
1999
- "1": {"User1": 3, "User2": 6},
2000
- "2": {"User1": 3, "User2": 6},
2001
- "3": {"User1": 3, "User2": 6},
2002
- "4": {"User1": 3, "User2": 5},
2003
- },
2004
- )
2005
-
2006
- # total user count = 40
2007
- dispatched_users = next(users_dispatcher)
2008
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 13, "User2": 27})
2009
- self.assertDictEqual(
2010
- dispatched_users,
2011
- {
2012
- "1": {"User1": 3, "User2": 7},
2013
- "2": {"User1": 4, "User2": 6},
2014
- "3": {"User1": 3, "User2": 7},
2015
- "4": {"User1": 3, "User2": 7},
2016
- },
2017
- )
2018
-
2019
- # total user count = 45
2020
- dispatched_users = next(users_dispatcher)
2021
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 15, "User2": 30})
2022
- self.assertDictEqual(
2023
- dispatched_users,
2024
- {
2025
- "1": {"User1": 4, "User2": 8},
2026
- "2": {"User1": 4, "User2": 7},
2027
- "3": {"User1": 3, "User2": 8},
2028
- "4": {"User1": 4, "User2": 7},
2029
- },
2030
- )
2031
-
2032
- # total user count = 50
2033
- dispatched_users = next(users_dispatcher)
2034
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 17, "User2": 33})
2035
- self.assertDictEqual(
2036
- dispatched_users,
2037
- {
2038
- "1": {"User1": 4, "User2": 9},
2039
- "2": {"User1": 5, "User2": 8},
2040
- "3": {"User1": 4, "User2": 8},
2041
- "4": {"User1": 4, "User2": 8},
2042
- },
2043
- )
2044
-
2045
- # total user count = 55
2046
- dispatched_users = next(users_dispatcher)
2047
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 18, "User2": 37})
2048
- self.assertDictEqual(
2049
- dispatched_users,
2050
- {
2051
- "1": {"User1": 5, "User2": 9},
2052
- "2": {"User1": 5, "User2": 9},
2053
- "3": {"User1": 4, "User2": 10},
2054
- "4": {"User1": 4, "User2": 9},
2055
- },
2056
- )
2057
-
2058
- # total user count = 60
2059
- dispatched_users = next(users_dispatcher)
2060
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 20, "User2": 40})
2061
- self.assertDictEqual(
2062
- dispatched_users,
2063
- {
2064
- "1": {"User1": 5, "User2": 10},
2065
- "2": {"User1": 5, "User2": 10},
2066
- "3": {"User1": 5, "User2": 10},
2067
- "4": {"User1": 5, "User2": 10},
2068
- },
2069
- )
2070
-
2071
- # total user count = 65
2072
- dispatched_users = next(users_dispatcher)
2073
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 22, "User2": 43})
2074
- self.assertDictEqual(
2075
- dispatched_users,
2076
- {
2077
- "1": {"User1": 6, "User2": 11},
2078
- "2": {"User1": 6, "User2": 10},
2079
- "3": {"User1": 5, "User2": 11},
2080
- "4": {"User1": 5, "User2": 11},
2081
- },
2082
- )
2083
-
2084
- # total user count = 70
2085
- dispatched_users = next(users_dispatcher)
2086
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 23, "User2": 47})
2087
- self.assertDictEqual(
2088
- dispatched_users,
2089
- {
2090
- "1": {"User1": 6, "User2": 12},
2091
- "2": {"User1": 6, "User2": 12},
2092
- "3": {"User1": 5, "User2": 12},
2093
- "4": {"User1": 6, "User2": 11},
2094
- },
2095
- )
2096
-
2097
- # total user count = 75, User1 = 25, User2 = 50
2098
- dispatched_users = next(users_dispatcher)
2099
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 25, "User2": 50})
2100
- self.assertDictEqual(
2101
- dispatched_users,
2102
- {
2103
- "1": {"User1": 6, "User2": 13},
2104
- "2": {"User1": 7, "User2": 12},
2105
- "3": {"User1": 6, "User2": 13},
2106
- "4": {"User1": 6, "User2": 12},
2107
- },
2108
- )
2109
-
2110
- self.assertRaises(StopIteration, lambda: next(users_dispatcher))
2111
-
2112
-
2113
- class TestLargeScale(unittest.TestCase):
2114
- # fmt: off
2115
- weights = [
2116
- 5, 55, 37, 2, 97, 41, 33, 19, 19, 34, 78, 76, 28, 62, 69, 5, 55, 37, 2, 97, 41, 33, 19, 19, 34,
2117
- 78, 76, 28, 62, 69, 41, 33, 19, 19, 34, 78, 76, 28, 62, 69, 41, 33, 19, 19, 34, 78, 76, 28, 62, 69
2118
- ]
2119
- # fmt: on
2120
- numerated_weights = dict(zip(range(len(weights)), weights))
2121
-
2122
- weighted_user_classes = [type(f"User{i}", (User,), {"weight": w}) for i, w in numerated_weights.items()]
2123
- fixed_user_classes_10k = [type(f"FixedUser10k{i}", (User,), {"fixed_count": 2000}) for i in range(50)]
2124
- fixed_user_classes_1M = [type(f"FixedUser1M{i}", (User,), {"fixed_count": 20000}) for i in range(50)]
2125
- mixed_users = weighted_user_classes[:25] + fixed_user_classes_10k[25:]
2126
-
2127
- def test_distribute_users(self):
2128
- for user_classes in [self.weighted_user_classes, self.fixed_user_classes_1M, self.mixed_users]:
2129
- workers = [WorkerNode(str(i)) for i in range(10_000)]
2130
-
2131
- target_user_count = 1_000_000
2132
-
2133
- users_dispatcher = UsersDispatcher(worker_nodes=workers, user_classes=user_classes)
2134
-
2135
- ts = time.perf_counter()
2136
- users_on_workers, user_gen, worker_gen, active_users = users_dispatcher._distribute_users(
2137
- target_user_count=target_user_count
2138
- )
2139
- delta = time.perf_counter() - ts
2140
-
2141
- # Because tests are run with coverage, the code will be slower.
2142
- # We set the pass criterion to 7000ms, but in real life, the
2143
- # `_distribute_users` method runs faster than this.
2144
- self.assertLessEqual(1000 * delta, 7000)
2145
-
2146
- self.assertEqual(_user_count(users_on_workers), target_user_count)
2147
-
2148
- def test_ramp_up_from_0_to_100_000_users_with_50_user_classes_and_1000_workers_and_5000_spawn_rate(self):
2149
- for user_classes in [
2150
- self.weighted_user_classes,
2151
- self.fixed_user_classes_1M,
2152
- self.fixed_user_classes_10k,
2153
- self.mixed_users,
2154
- ]:
2155
- workers = [WorkerNode(str(i)) for i in range(1000)]
2156
-
2157
- target_user_count = 100_000
2158
-
2159
- users_dispatcher = UsersDispatcher(worker_nodes=workers, user_classes=user_classes)
2160
- users_dispatcher.new_dispatch(target_user_count=target_user_count, spawn_rate=5_000)
2161
- users_dispatcher._wait_between_dispatch = 0
2162
-
2163
- all_dispatched_users = list(users_dispatcher)
2164
-
2165
- tol = 0.2
2166
- self.assertTrue(
2167
- all(
2168
- dispatch_iteration_duration <= tol
2169
- for dispatch_iteration_duration in users_dispatcher.dispatch_iteration_durations
2170
- ),
2171
- f"One or more dispatch took more than {tol * 1000:.0f}s to compute (max = {1000 * max(users_dispatcher.dispatch_iteration_durations)}ms)",
2172
- )
2173
-
2174
- self.assertEqual(_user_count(all_dispatched_users[-1]), target_user_count)
2175
-
2176
- for dispatch_users in all_dispatched_users:
2177
- user_count_on_workers = [
2178
- sum(user_classes_count.values()) for user_classes_count in dispatch_users.values()
2179
- ]
2180
- self.assertLessEqual(
2181
- max(user_count_on_workers) - min(user_count_on_workers),
2182
- 1,
2183
- f"One or more workers have too much users compared to the other workers when user count is {_user_count(dispatch_users)}",
2184
- )
2185
-
2186
- for i, dispatch_users in enumerate(all_dispatched_users):
2187
- aggregated_dispatched_users = _aggregate_dispatched_users(dispatch_users)
2188
- for user_class in [u for u in user_classes if not u.fixed_count]:
2189
- target_relative_weight = user_class.weight / sum(
2190
- map(attrgetter("weight"), [u for u in user_classes if not u.fixed_count])
2191
- )
2192
- relative_weight = aggregated_dispatched_users[user_class.__name__] / _user_count(dispatch_users)
2193
- error_percent = 100 * (relative_weight - target_relative_weight) / target_relative_weight
2194
- if i == len(all_dispatched_users) - 1:
2195
- # We want the distribution to be as good as possible at the end of the ramp-up
2196
- tol = 0.5
2197
- else:
2198
- tol = 15
2199
- self.assertLessEqual(
2200
- error_percent,
2201
- tol,
2202
- f"Distribution for user class {user_class} is off by more than {tol}% when user count is {_user_count(dispatch_users)}",
2203
- )
2204
-
2205
- def test_ramp_down_from_100_000_to_0_users_with_50_user_classes_and_1000_workers_and_5000_spawn_rate(self):
2206
- for user_classes in [
2207
- self.weighted_user_classes,
2208
- self.fixed_user_classes_1M,
2209
- self.fixed_user_classes_10k,
2210
- self.mixed_users,
2211
- ]:
2212
- initial_user_count = 100_000
2213
-
2214
- workers = [WorkerNode(str(i)) for i in range(1000)]
2215
-
2216
- # Ramp-up
2217
- users_dispatcher = UsersDispatcher(worker_nodes=workers, user_classes=user_classes)
2218
- users_dispatcher.new_dispatch(target_user_count=initial_user_count, spawn_rate=initial_user_count)
2219
- users_dispatcher._wait_between_dispatch = 0
2220
- list(users_dispatcher)
2221
-
2222
- # Ramp-down
2223
- users_dispatcher.new_dispatch(target_user_count=0, spawn_rate=5000)
2224
- users_dispatcher._wait_between_dispatch = 0
2225
-
2226
- all_dispatched_users = list(users_dispatcher)
2227
-
2228
- tol = 0.2
2229
- self.assertTrue(
2230
- all(
2231
- dispatch_iteration_duration <= tol
2232
- for dispatch_iteration_duration in users_dispatcher.dispatch_iteration_durations
2233
- ),
2234
- f"One or more dispatch took more than {tol * 1000:.0f}ms to compute (max = {1000 * max(users_dispatcher.dispatch_iteration_durations)}ms)",
2235
- )
2236
-
2237
- self.assertEqual(_user_count(all_dispatched_users[-1]), 0)
2238
-
2239
- for dispatch_users in all_dispatched_users[:-1]:
2240
- user_count_on_workers = [
2241
- sum(user_classes_count.values()) for user_classes_count in dispatch_users.values()
2242
- ]
2243
- self.assertLessEqual(
2244
- max(user_count_on_workers) - min(user_count_on_workers),
2245
- 1,
2246
- f"One or more workers have too much users compared to the other workers when user count is {_user_count(dispatch_users)}",
2247
- )
2248
-
2249
- for dispatch_users in all_dispatched_users[:-1]:
2250
- aggregated_dispatched_users = _aggregate_dispatched_users(dispatch_users)
2251
- for user_class in [u for u in user_classes if not u.fixed_count]:
2252
- target_relative_weight = user_class.weight / sum(
2253
- map(attrgetter("weight"), [u for u in user_classes if not u.fixed_count])
2254
- )
2255
- relative_weight = aggregated_dispatched_users[user_class.__name__] / _user_count(dispatch_users)
2256
- error_percent = 100 * (relative_weight - target_relative_weight) / target_relative_weight
2257
- tol = 15
2258
- self.assertLessEqual(
2259
- error_percent,
2260
- tol,
2261
- f"Distribution for user class {user_class} is off by more than {tol}% when user count is {_user_count(dispatch_users)}",
2262
- )
2263
-
2264
-
2265
- class TestSmallConsecutiveRamping(unittest.TestCase):
2266
- def test_consecutive_ramp_up_and_ramp_down(self):
2267
- class User1(User):
2268
- weight = 1
2269
-
2270
- class User2(User):
2271
- weight = 1
2272
-
2273
- user_classes = [User1, User2]
2274
-
2275
- worker_node1 = WorkerNode("1")
2276
- worker_node2 = WorkerNode("2")
2277
-
2278
- worker_nodes = [worker_node1, worker_node2]
2279
-
2280
- users_dispatcher = UsersDispatcher(worker_nodes=worker_nodes, user_classes=user_classes)
2281
-
2282
- # user count = 1
2283
- users_dispatcher.new_dispatch(target_user_count=1, spawn_rate=1)
2284
- users_dispatcher._wait_between_dispatch = 0
2285
-
2286
- dispatched_users = next(users_dispatcher)
2287
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 1, "User2": 0})
2288
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_node1.id), 1)
2289
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_node2.id), 0)
2290
-
2291
- # user count = 2
2292
- users_dispatcher.new_dispatch(target_user_count=2, spawn_rate=1)
2293
- users_dispatcher._wait_between_dispatch = 0
2294
-
2295
- dispatched_users = next(users_dispatcher)
2296
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 1, "User2": 1})
2297
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_node1.id), 1)
2298
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_node2.id), 1)
2299
-
2300
- # user count = 3
2301
- users_dispatcher.new_dispatch(target_user_count=3, spawn_rate=1)
2302
- users_dispatcher._wait_between_dispatch = 0
2303
-
2304
- dispatched_users = next(users_dispatcher)
2305
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 1})
2306
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_node1.id), 2)
2307
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_node2.id), 1)
2308
-
2309
- # user count = 4
2310
- users_dispatcher.new_dispatch(target_user_count=4, spawn_rate=1)
2311
- users_dispatcher._wait_between_dispatch = 0
2312
-
2313
- dispatched_users = next(users_dispatcher)
2314
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 2})
2315
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_node1.id), 2)
2316
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_node2.id), 2)
2317
-
2318
- # user count = 3
2319
- users_dispatcher.new_dispatch(target_user_count=3, spawn_rate=1)
2320
- users_dispatcher._wait_between_dispatch = 0
2321
-
2322
- dispatched_users = next(users_dispatcher)
2323
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 1})
2324
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_node1.id), 2)
2325
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_node2.id), 1)
2326
-
2327
- # user count = 2
2328
- users_dispatcher.new_dispatch(target_user_count=2, spawn_rate=1)
2329
- users_dispatcher._wait_between_dispatch = 0
2330
-
2331
- dispatched_users = next(users_dispatcher)
2332
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 1, "User2": 1})
2333
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_node1.id), 1)
2334
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_node2.id), 1)
2335
-
2336
- # user count = 1
2337
- users_dispatcher.new_dispatch(target_user_count=1, spawn_rate=1)
2338
- users_dispatcher._wait_between_dispatch = 0
2339
-
2340
- dispatched_users = next(users_dispatcher)
2341
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 1, "User2": 0})
2342
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_node1.id), 1)
2343
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_node2.id), 0)
2344
-
2345
- # user count = 0
2346
- users_dispatcher.new_dispatch(target_user_count=0, spawn_rate=1)
2347
- users_dispatcher._wait_between_dispatch = 0
2348
-
2349
- dispatched_users = next(users_dispatcher)
2350
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 0, "User2": 0})
2351
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_node1.id), 0)
2352
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_node2.id), 0)
2353
-
2354
-
2355
- class TestRampingMiscellaneous(unittest.TestCase):
2356
- def test_spawn_rate_greater_than_target_user_count(self):
2357
- class User1(User):
2358
- weight = 1
2359
-
2360
- user_classes = [User1]
2361
-
2362
- worker_nodes = [WorkerNode(str(i + 1)) for i in range(1)]
2363
-
2364
- users_dispatcher = UsersDispatcher(worker_nodes=worker_nodes, user_classes=user_classes)
2365
-
2366
- users_dispatcher.new_dispatch(target_user_count=1, spawn_rate=100)
2367
- users_dispatcher._wait_between_dispatch = 0
2368
- dispatched_users = next(users_dispatcher)
2369
- self.assertDictEqual(dispatched_users, {"1": {"User1": 1}})
2370
-
2371
- users_dispatcher.new_dispatch(target_user_count=11, spawn_rate=100)
2372
- users_dispatcher._wait_between_dispatch = 0
2373
- dispatched_users = next(users_dispatcher)
2374
- self.assertDictEqual(dispatched_users, {"1": {"User1": 11}})
2375
-
2376
- users_dispatcher.new_dispatch(target_user_count=10, spawn_rate=100)
2377
- users_dispatcher._wait_between_dispatch = 0
2378
- dispatched_users = next(users_dispatcher)
2379
- self.assertDictEqual(dispatched_users, {"1": {"User1": 10}})
2380
-
2381
- users_dispatcher.new_dispatch(target_user_count=0, spawn_rate=100)
2382
- users_dispatcher._wait_between_dispatch = 0
2383
- dispatched_users = next(users_dispatcher)
2384
- self.assertDictEqual(dispatched_users, {"1": {"User1": 0}})
2385
-
2386
-
2387
- class TestRemoveWorker(unittest.TestCase):
2388
- def test_remove_worker_during_ramp_up(self):
2389
- class User1(User):
2390
- weight = 1
2391
-
2392
- class User2(User):
2393
- weight = 1
2394
-
2395
- class User3(User):
2396
- weight = 1
2397
-
2398
- user_classes = [User1, User2, User3]
2399
-
2400
- worker_nodes = [WorkerNode(str(i + 1)) for i in range(3)]
2401
-
2402
- users_dispatcher = UsersDispatcher(worker_nodes=worker_nodes, user_classes=user_classes)
2403
-
2404
- sleep_time = 0.2 # Speed-up test
2405
-
2406
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=3)
2407
- users_dispatcher._wait_between_dispatch = sleep_time
2408
-
2409
- # Dispatch iteration 1
2410
- ts = time.perf_counter()
2411
- dispatched_users = next(users_dispatcher)
2412
- delta = time.perf_counter() - ts
2413
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
2414
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 1, "User2": 1, "User3": 1})
2415
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 1)
2416
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 1)
2417
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 1)
2418
-
2419
- # Dispatch iteration 2
2420
- ts = time.perf_counter()
2421
- dispatched_users = next(users_dispatcher)
2422
- delta = time.perf_counter() - ts
2423
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
2424
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 2, "User3": 2})
2425
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 2)
2426
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 2)
2427
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 2)
2428
-
2429
- self.assertFalse(users_dispatcher._rebalance)
2430
-
2431
- users_dispatcher.remove_worker(worker_nodes[1])
2432
-
2433
- self.assertTrue(users_dispatcher._rebalance)
2434
-
2435
- # Re-balance
2436
- ts = time.perf_counter()
2437
- dispatched_users = next(users_dispatcher)
2438
- delta = time.perf_counter() - ts
2439
- self.assertTrue(0 <= delta <= _TOLERANCE, f"Expected re-balance dispatch to be instantaneous but got {delta}s")
2440
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 2, "User3": 2})
2441
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 3)
2442
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 3)
2443
-
2444
- self.assertFalse(users_dispatcher._rebalance)
2445
-
2446
- # Dispatch iteration 3
2447
- ts = time.perf_counter()
2448
- dispatched_users = next(users_dispatcher)
2449
- delta = time.perf_counter() - ts
2450
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
2451
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 3, "User2": 3, "User3": 3})
2452
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 5)
2453
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 4)
2454
-
2455
- def test_remove_two_workers_during_ramp_up(self):
2456
- class User1(User):
2457
- weight = 1
2458
-
2459
- class User2(User):
2460
- weight = 1
2461
-
2462
- class User3(User):
2463
- weight = 1
2464
-
2465
- user_classes = [User1, User2, User3]
2466
-
2467
- worker_nodes = [WorkerNode(str(i + 1)) for i in range(3)]
2468
-
2469
- users_dispatcher = UsersDispatcher(worker_nodes=worker_nodes, user_classes=user_classes)
2470
-
2471
- sleep_time = 0.2 # Speed-up test
2472
-
2473
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=3)
2474
- users_dispatcher._wait_between_dispatch = sleep_time
2475
-
2476
- # Dispatch iteration 1
2477
- ts = time.perf_counter()
2478
- dispatched_users = next(users_dispatcher)
2479
- delta = time.perf_counter() - ts
2480
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
2481
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 1, "User2": 1, "User3": 1})
2482
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 1)
2483
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 1)
2484
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 1)
2485
-
2486
- # Dispatch iteration 2
2487
- ts = time.perf_counter()
2488
- dispatched_users = next(users_dispatcher)
2489
- delta = time.perf_counter() - ts
2490
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
2491
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 2, "User3": 2})
2492
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 2)
2493
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 2)
2494
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 2)
2495
-
2496
- self.assertFalse(users_dispatcher._rebalance)
2497
-
2498
- users_dispatcher.remove_worker(worker_nodes[1])
2499
- users_dispatcher.remove_worker(worker_nodes[2])
2500
-
2501
- self.assertTrue(users_dispatcher._rebalance)
2502
-
2503
- # Re-balance
2504
- ts = time.perf_counter()
2505
- dispatched_users = next(users_dispatcher)
2506
- delta = time.perf_counter() - ts
2507
- self.assertTrue(0 <= delta <= _TOLERANCE, f"Expected re-balance dispatch to be instantaneous but got {delta}s")
2508
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 2, "User3": 2})
2509
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 6)
2510
-
2511
- self.assertFalse(users_dispatcher._rebalance)
2512
-
2513
- # Dispatch iteration 3
2514
- ts = time.perf_counter()
2515
- dispatched_users = next(users_dispatcher)
2516
- delta = time.perf_counter() - ts
2517
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
2518
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 3, "User2": 3, "User3": 3})
2519
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 9)
2520
-
2521
- def test_remove_worker_between_two_ramp_ups(self):
2522
- class User1(User):
2523
- weight = 1
2524
-
2525
- class User2(User):
2526
- weight = 1
2527
-
2528
- class User3(User):
2529
- weight = 1
2530
-
2531
- user_classes = [User1, User2, User3]
2532
-
2533
- worker_nodes = [WorkerNode(str(i + 1)) for i in range(3)]
2534
-
2535
- users_dispatcher = UsersDispatcher(worker_nodes=worker_nodes, user_classes=user_classes)
2536
-
2537
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=3)
2538
- users_dispatcher._wait_between_dispatch = 0
2539
-
2540
- list(users_dispatcher)
2541
-
2542
- self.assertFalse(users_dispatcher._rebalance)
2543
-
2544
- users_dispatcher.remove_worker(worker_nodes[1])
2545
-
2546
- self.assertTrue(users_dispatcher._rebalance)
2547
-
2548
- sleep_time = 0.2 # Speed-up test
2549
-
2550
- users_dispatcher.new_dispatch(target_user_count=18, spawn_rate=3)
2551
- users_dispatcher._wait_between_dispatch = sleep_time
2552
-
2553
- self.assertTrue(users_dispatcher._rebalance)
2554
-
2555
- # Re-balance
2556
- ts = time.perf_counter()
2557
- dispatched_users = next(users_dispatcher)
2558
- delta = time.perf_counter() - ts
2559
- self.assertTrue(0 <= delta <= _TOLERANCE, f"Expected re-balance dispatch to be instantaneous but got {delta}s")
2560
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 3, "User2": 3, "User3": 3})
2561
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 5)
2562
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 4)
2563
-
2564
- self.assertFalse(users_dispatcher._rebalance)
2565
-
2566
- # Dispatch iteration 1
2567
- ts = time.perf_counter()
2568
- dispatched_users = next(users_dispatcher)
2569
- delta = time.perf_counter() - ts
2570
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
2571
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 4, "User2": 4, "User3": 4})
2572
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 6)
2573
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 6)
2574
-
2575
- # Dispatch iteration 2
2576
- ts = time.perf_counter()
2577
- dispatched_users = next(users_dispatcher)
2578
- delta = time.perf_counter() - ts
2579
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
2580
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 5, "User2": 5, "User3": 5})
2581
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 8)
2582
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 7)
2583
-
2584
- # Dispatch iteration 3
2585
- ts = time.perf_counter()
2586
- dispatched_users = next(users_dispatcher)
2587
- delta = time.perf_counter() - ts
2588
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
2589
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 6, "User2": 6, "User3": 6})
2590
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 9)
2591
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 9)
2592
-
2593
- def test_remove_two_workers_between_two_ramp_ups(self):
2594
- class User1(User):
2595
- weight = 1
2596
-
2597
- class User2(User):
2598
- weight = 1
2599
-
2600
- class User3(User):
2601
- weight = 1
2602
-
2603
- user_classes = [User1, User2, User3]
2604
-
2605
- worker_nodes = [WorkerNode(str(i + 1)) for i in range(3)]
2606
-
2607
- users_dispatcher = UsersDispatcher(worker_nodes=worker_nodes, user_classes=user_classes)
2608
-
2609
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=3)
2610
- users_dispatcher._wait_between_dispatch = 0
2611
-
2612
- list(users_dispatcher)
2613
-
2614
- self.assertFalse(users_dispatcher._rebalance)
2615
-
2616
- users_dispatcher.remove_worker(worker_nodes[1])
2617
- users_dispatcher.remove_worker(worker_nodes[2])
2618
-
2619
- self.assertTrue(users_dispatcher._rebalance)
2620
-
2621
- sleep_time = 0.2 # Speed-up test
2622
-
2623
- users_dispatcher.new_dispatch(target_user_count=18, spawn_rate=3)
2624
- users_dispatcher._wait_between_dispatch = sleep_time
2625
-
2626
- self.assertTrue(users_dispatcher._rebalance)
2627
-
2628
- # Re-balance
2629
- ts = time.perf_counter()
2630
- dispatched_users = next(users_dispatcher)
2631
- delta = time.perf_counter() - ts
2632
- self.assertTrue(0 <= delta <= _TOLERANCE, f"Expected re-balance dispatch to be instantaneous but got {delta}s")
2633
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 3, "User2": 3, "User3": 3})
2634
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 9)
2635
-
2636
- self.assertFalse(users_dispatcher._rebalance)
2637
-
2638
- # Dispatch iteration 1
2639
- ts = time.perf_counter()
2640
- dispatched_users = next(users_dispatcher)
2641
- delta = time.perf_counter() - ts
2642
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
2643
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 4, "User2": 4, "User3": 4})
2644
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 12)
2645
-
2646
- # Dispatch iteration 2
2647
- ts = time.perf_counter()
2648
- dispatched_users = next(users_dispatcher)
2649
- delta = time.perf_counter() - ts
2650
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
2651
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 5, "User2": 5, "User3": 5})
2652
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 15)
2653
-
2654
- # Dispatch iteration 3
2655
- ts = time.perf_counter()
2656
- dispatched_users = next(users_dispatcher)
2657
- delta = time.perf_counter() - ts
2658
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
2659
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 6, "User2": 6, "User3": 6})
2660
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 18)
2661
-
2662
- def test_remove_worker_during_ramp_down(self):
2663
- class User1(User):
2664
- weight = 1
2665
-
2666
- class User2(User):
2667
- weight = 1
2668
-
2669
- class User3(User):
2670
- weight = 1
2671
-
2672
- user_classes = [User1, User2, User3]
2673
-
2674
- worker_nodes = [WorkerNode(str(i + 1)) for i in range(3)]
2675
-
2676
- users_dispatcher = UsersDispatcher(worker_nodes=worker_nodes, user_classes=user_classes)
2677
-
2678
- users_dispatcher.new_dispatch(target_user_count=18, spawn_rate=3)
2679
- users_dispatcher._wait_between_dispatch = 0
2680
- list(users_dispatcher)
2681
-
2682
- sleep_time = 0.2 # Speed-up test
2683
-
2684
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=3)
2685
- users_dispatcher._wait_between_dispatch = sleep_time
2686
-
2687
- # Dispatch iteration 1
2688
- ts = time.perf_counter()
2689
- dispatched_users = next(users_dispatcher)
2690
- delta = time.perf_counter() - ts
2691
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
2692
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 5, "User2": 5, "User3": 5})
2693
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 5)
2694
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 5)
2695
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 5)
2696
-
2697
- # Dispatch iteration 2
2698
- ts = time.perf_counter()
2699
- dispatched_users = next(users_dispatcher)
2700
- delta = time.perf_counter() - ts
2701
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
2702
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 4, "User2": 4, "User3": 4})
2703
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 4)
2704
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 4)
2705
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 4)
2706
-
2707
- self.assertFalse(users_dispatcher._rebalance)
2708
-
2709
- users_dispatcher.remove_worker(worker_nodes[1])
2710
-
2711
- self.assertTrue(users_dispatcher._rebalance)
2712
-
2713
- # Re-balance
2714
- ts = time.perf_counter()
2715
- dispatched_users = next(users_dispatcher)
2716
- delta = time.perf_counter() - ts
2717
- self.assertTrue(0 <= delta <= _TOLERANCE, f"Expected re-balance dispatch to be instantaneous but got {delta}s")
2718
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 4, "User2": 4, "User3": 4})
2719
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 6)
2720
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 6)
2721
-
2722
- self.assertFalse(users_dispatcher._rebalance)
2723
-
2724
- # Dispatch iteration 3
2725
- ts = time.perf_counter()
2726
- dispatched_users = next(users_dispatcher)
2727
- delta = time.perf_counter() - ts
2728
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
2729
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 3, "User2": 3, "User3": 3})
2730
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 5)
2731
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 4)
2732
-
2733
- def test_remove_two_workers_during_ramp_down(self):
2734
- class User1(User):
2735
- weight = 1
2736
-
2737
- class User2(User):
2738
- weight = 1
2739
-
2740
- class User3(User):
2741
- weight = 1
2742
-
2743
- user_classes = [User1, User2, User3]
2744
-
2745
- worker_nodes = [WorkerNode(str(i + 1)) for i in range(3)]
2746
-
2747
- users_dispatcher = UsersDispatcher(worker_nodes=worker_nodes, user_classes=user_classes)
2748
-
2749
- users_dispatcher.new_dispatch(target_user_count=18, spawn_rate=3)
2750
- users_dispatcher._wait_between_dispatch = 0
2751
- list(users_dispatcher)
2752
-
2753
- sleep_time = 0.2 # Speed-up test
2754
-
2755
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=3)
2756
- users_dispatcher._wait_between_dispatch = sleep_time
2757
-
2758
- # Dispatch iteration 1
2759
- ts = time.perf_counter()
2760
- dispatched_users = next(users_dispatcher)
2761
- delta = time.perf_counter() - ts
2762
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
2763
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 5, "User2": 5, "User3": 5})
2764
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 5)
2765
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 5)
2766
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 5)
2767
-
2768
- # Dispatch iteration 2
2769
- ts = time.perf_counter()
2770
- dispatched_users = next(users_dispatcher)
2771
- delta = time.perf_counter() - ts
2772
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
2773
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 4, "User2": 4, "User3": 4})
2774
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 4)
2775
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 4)
2776
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 4)
2777
-
2778
- self.assertFalse(users_dispatcher._rebalance)
2779
-
2780
- users_dispatcher.remove_worker(worker_nodes[1])
2781
- users_dispatcher.remove_worker(worker_nodes[2])
2782
-
2783
- self.assertTrue(users_dispatcher._rebalance)
2784
-
2785
- # Re-balance
2786
- ts = time.perf_counter()
2787
- dispatched_users = next(users_dispatcher)
2788
- delta = time.perf_counter() - ts
2789
- self.assertTrue(0 <= delta <= _TOLERANCE, f"Expected re-balance dispatch to be instantaneous but got {delta}s")
2790
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 4, "User2": 4, "User3": 4})
2791
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 12)
2792
-
2793
- self.assertFalse(users_dispatcher._rebalance)
2794
-
2795
- # Dispatch iteration 3
2796
- ts = time.perf_counter()
2797
- dispatched_users = next(users_dispatcher)
2798
- delta = time.perf_counter() - ts
2799
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
2800
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 3, "User2": 3, "User3": 3})
2801
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 9)
2802
-
2803
- def test_remove_last_worker(self):
2804
- class User1(User):
2805
- weight = 1
2806
-
2807
- class User2(User):
2808
- weight = 1
2809
-
2810
- class User3(User):
2811
- weight = 1
2812
-
2813
- user_classes = [User1, User2, User3]
2814
-
2815
- worker_nodes = [WorkerNode(str(i + 1)) for i in range(1)]
2816
-
2817
- users_dispatcher = UsersDispatcher(worker_nodes=worker_nodes, user_classes=user_classes)
2818
-
2819
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=3)
2820
- users_dispatcher._wait_between_dispatch = 0
2821
-
2822
- # Dispatch iteration 1
2823
- dispatched_users = next(users_dispatcher)
2824
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 1, "User2": 1, "User3": 1})
2825
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 3)
2826
-
2827
- # Dispatch iteration 2
2828
- dispatched_users = next(users_dispatcher)
2829
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 2, "User3": 2})
2830
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 6)
2831
-
2832
- self.assertFalse(users_dispatcher._rebalance)
2833
-
2834
- users_dispatcher.remove_worker(worker_nodes[0])
2835
-
2836
- self.assertFalse(users_dispatcher._rebalance)
2837
-
2838
- def test_remove_worker_during_ramp_up_with_fixed_user(self):
2839
- class User1(User):
2840
- fixed_count = 2
2841
-
2842
- class User2(User):
2843
- weight = 1
2844
-
2845
- class User3(User):
2846
- weight = 1
2847
-
2848
- user_classes = [User1, User2, User3]
2849
-
2850
- worker_nodes = [WorkerNode(str(i + 1)) for i in range(3)]
2851
-
2852
- users_dispatcher = UsersDispatcher(worker_nodes=worker_nodes, user_classes=user_classes)
2853
-
2854
- sleep_time = 0.2 # Speed-up test
2855
-
2856
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=3)
2857
- users_dispatcher._wait_between_dispatch = sleep_time
2858
-
2859
- # Dispatch iteration 1
2860
- ts = time.perf_counter()
2861
- dispatched_users = next(users_dispatcher)
2862
- delta = time.perf_counter() - ts
2863
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
2864
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 1, "User3": 0})
2865
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 1)
2866
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 1)
2867
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 1)
2868
-
2869
- # Dispatch iteration 2
2870
- ts = time.perf_counter()
2871
- dispatched_users = next(users_dispatcher)
2872
- delta = time.perf_counter() - ts
2873
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
2874
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 2, "User3": 2})
2875
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 2)
2876
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 2)
2877
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 2)
2878
-
2879
- self.assertFalse(users_dispatcher._rebalance)
2880
-
2881
- users_dispatcher.remove_worker(worker_nodes[1])
2882
-
2883
- self.assertTrue(users_dispatcher._rebalance)
2884
-
2885
- # Re-balance
2886
- ts = time.perf_counter()
2887
- dispatched_users = next(users_dispatcher)
2888
- delta = time.perf_counter() - ts
2889
- self.assertTrue(0 <= delta <= _TOLERANCE, f"Expected re-balance dispatch to be instantaneous but got {delta}s")
2890
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 2, "User3": 2})
2891
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 3)
2892
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 3)
2893
-
2894
- self.assertFalse(users_dispatcher._rebalance)
2895
-
2896
- # Dispatch iteration 3
2897
- ts = time.perf_counter()
2898
- dispatched_users = next(users_dispatcher)
2899
- delta = time.perf_counter() - ts
2900
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
2901
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 4, "User3": 3})
2902
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 5)
2903
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 4)
2904
-
2905
-
2906
- class TestAddWorker(unittest.TestCase):
2907
- def test_add_worker_during_ramp_up(self):
2908
- class User1(User):
2909
- weight = 1
2910
-
2911
- class User2(User):
2912
- weight = 1
2913
-
2914
- class User3(User):
2915
- weight = 1
2916
-
2917
- user_classes = [User1, User2, User3]
2918
-
2919
- worker_nodes = [
2920
- WorkerNode("hostname1_worker1"),
2921
- WorkerNode("hostname1_worker2"),
2922
- WorkerNode("hostname2_worker1"),
2923
- ]
2924
-
2925
- users_dispatcher = UsersDispatcher(worker_nodes=[worker_nodes[0], worker_nodes[2]], user_classes=user_classes)
2926
-
2927
- sleep_time = 0.2 # Speed-up test
2928
-
2929
- users_dispatcher.new_dispatch(target_user_count=11, spawn_rate=3)
2930
- users_dispatcher._wait_between_dispatch = sleep_time
2931
-
2932
- # Dispatch iteration 1
2933
- ts = time.perf_counter()
2934
- dispatched_users = next(users_dispatcher)
2935
- delta = time.perf_counter() - ts
2936
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
2937
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 1, "User2": 1, "User3": 1})
2938
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 2)
2939
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 1)
2940
-
2941
- # Dispatch iteration 2
2942
- ts = time.perf_counter()
2943
- dispatched_users = next(users_dispatcher)
2944
- delta = time.perf_counter() - ts
2945
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
2946
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 2, "User3": 2})
2947
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 3)
2948
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 3)
2949
-
2950
- self.assertFalse(users_dispatcher._rebalance)
2951
-
2952
- users_dispatcher.add_worker(worker_nodes[1])
2953
-
2954
- self.assertTrue(users_dispatcher._rebalance)
2955
-
2956
- # Re-balance
2957
- ts = time.perf_counter()
2958
- dispatched_users = next(users_dispatcher)
2959
- delta = time.perf_counter() - ts
2960
- self.assertTrue(0 <= delta <= _TOLERANCE, f"Expected re-balance dispatch to be instantaneous but got {delta}s")
2961
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 2, "User3": 2})
2962
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 2)
2963
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 2)
2964
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 2)
2965
-
2966
- self.assertFalse(users_dispatcher._rebalance)
2967
-
2968
- # Dispatch iteration 3
2969
- ts = time.perf_counter()
2970
- dispatched_users = next(users_dispatcher)
2971
- delta = time.perf_counter() - ts
2972
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
2973
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 3, "User2": 3, "User3": 3})
2974
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 3)
2975
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 3)
2976
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 3)
2977
-
2978
- # Dispatch iteration 4
2979
- ts = time.perf_counter()
2980
- dispatched_users = next(users_dispatcher)
2981
- delta = time.perf_counter() - ts
2982
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
2983
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 4, "User2": 4, "User3": 3})
2984
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 4)
2985
- # without host-based balancing the following two values would be reversed
2986
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 3)
2987
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 4)
2988
-
2989
- def test_add_two_workers_during_ramp_up(self):
2990
- class User1(User):
2991
- weight = 1
2992
-
2993
- class User2(User):
2994
- weight = 1
2995
-
2996
- class User3(User):
2997
- weight = 1
2998
-
2999
- user_classes = [User1, User2, User3]
3000
-
3001
- worker_nodes = [WorkerNode(str(i + 1)) for i in range(3)]
3002
-
3003
- users_dispatcher = UsersDispatcher(worker_nodes=[worker_nodes[0]], user_classes=user_classes)
3004
-
3005
- sleep_time = 0.2 # Speed-up test
3006
-
3007
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=3)
3008
- users_dispatcher._wait_between_dispatch = sleep_time
3009
-
3010
- # Dispatch iteration 1
3011
- ts = time.perf_counter()
3012
- dispatched_users = next(users_dispatcher)
3013
- delta = time.perf_counter() - ts
3014
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
3015
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 1, "User2": 1, "User3": 1})
3016
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 3)
3017
-
3018
- # Dispatch iteration 2
3019
- ts = time.perf_counter()
3020
- dispatched_users = next(users_dispatcher)
3021
- delta = time.perf_counter() - ts
3022
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
3023
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 2, "User3": 2})
3024
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 6)
3025
-
3026
- self.assertFalse(users_dispatcher._rebalance)
3027
-
3028
- users_dispatcher.add_worker(worker_nodes[1])
3029
- users_dispatcher.add_worker(worker_nodes[2])
3030
-
3031
- self.assertTrue(users_dispatcher._rebalance)
3032
-
3033
- # Re-balance
3034
- ts = time.perf_counter()
3035
- dispatched_users = next(users_dispatcher)
3036
- delta = time.perf_counter() - ts
3037
- self.assertTrue(0 <= delta <= _TOLERANCE, f"Expected re-balance dispatch to be instantaneous but got {delta}s")
3038
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 2, "User3": 2})
3039
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 2)
3040
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 2)
3041
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 2)
3042
-
3043
- self.assertFalse(users_dispatcher._rebalance)
3044
-
3045
- # Dispatch iteration 3
3046
- ts = time.perf_counter()
3047
- dispatched_users = next(users_dispatcher)
3048
- delta = time.perf_counter() - ts
3049
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
3050
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 3, "User2": 3, "User3": 3})
3051
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 3)
3052
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 3)
3053
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 3)
3054
-
3055
- def test_add_worker_between_two_ramp_ups(self):
3056
- class User1(User):
3057
- weight = 1
3058
-
3059
- class User2(User):
3060
- weight = 1
3061
-
3062
- class User3(User):
3063
- weight = 1
3064
-
3065
- user_classes = [User1, User2, User3]
3066
-
3067
- worker_nodes = [WorkerNode(str(i + 1)) for i in range(3)]
3068
-
3069
- users_dispatcher = UsersDispatcher(worker_nodes=[worker_nodes[0], worker_nodes[2]], user_classes=user_classes)
3070
-
3071
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=3)
3072
- users_dispatcher._wait_between_dispatch = 0
3073
-
3074
- list(users_dispatcher)
3075
-
3076
- self.assertFalse(users_dispatcher._rebalance)
3077
-
3078
- users_dispatcher.add_worker(worker_nodes[1])
3079
-
3080
- self.assertTrue(users_dispatcher._rebalance)
3081
-
3082
- sleep_time = 0.2 # Speed-up test
3083
-
3084
- users_dispatcher.new_dispatch(target_user_count=18, spawn_rate=3)
3085
- users_dispatcher._wait_between_dispatch = sleep_time
3086
-
3087
- self.assertTrue(users_dispatcher._rebalance)
3088
-
3089
- # Re-balance
3090
- ts = time.perf_counter()
3091
- dispatched_users = next(users_dispatcher)
3092
- delta = time.perf_counter() - ts
3093
- self.assertTrue(0 <= delta <= _TOLERANCE, f"Expected re-balance dispatch to be instantaneous but got {delta}s")
3094
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 3, "User2": 3, "User3": 3})
3095
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 3)
3096
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 3)
3097
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 3)
3098
-
3099
- self.assertFalse(users_dispatcher._rebalance)
3100
-
3101
- # Dispatch iteration 1
3102
- ts = time.perf_counter()
3103
- dispatched_users = next(users_dispatcher)
3104
- delta = time.perf_counter() - ts
3105
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
3106
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 4, "User2": 4, "User3": 4})
3107
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 4)
3108
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 4)
3109
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 4)
3110
-
3111
- # Dispatch iteration 2
3112
- ts = time.perf_counter()
3113
- dispatched_users = next(users_dispatcher)
3114
- delta = time.perf_counter() - ts
3115
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
3116
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 5, "User2": 5, "User3": 5})
3117
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 5)
3118
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 5)
3119
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 5)
3120
-
3121
- # Dispatch iteration 3
3122
- ts = time.perf_counter()
3123
- dispatched_users = next(users_dispatcher)
3124
- delta = time.perf_counter() - ts
3125
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
3126
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 6, "User2": 6, "User3": 6})
3127
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 6)
3128
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 6)
3129
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 6)
3130
-
3131
- def test_add_two_workers_between_two_ramp_ups(self):
3132
- class User1(User):
3133
- weight = 1
3134
-
3135
- class User2(User):
3136
- weight = 1
3137
-
3138
- class User3(User):
3139
- weight = 1
3140
-
3141
- user_classes = [User1, User2, User3]
3142
-
3143
- worker_nodes = [WorkerNode(str(i + 1)) for i in range(3)]
3144
-
3145
- users_dispatcher = UsersDispatcher(worker_nodes=[worker_nodes[0]], user_classes=user_classes)
3146
-
3147
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=3)
3148
- users_dispatcher._wait_between_dispatch = 0
3149
-
3150
- list(users_dispatcher)
3151
-
3152
- self.assertFalse(users_dispatcher._rebalance)
3153
-
3154
- users_dispatcher.add_worker(worker_nodes[1])
3155
- users_dispatcher.add_worker(worker_nodes[2])
3156
-
3157
- self.assertTrue(users_dispatcher._rebalance)
3158
-
3159
- sleep_time = 0.2 # Speed-up test
3160
-
3161
- users_dispatcher.new_dispatch(target_user_count=18, spawn_rate=3)
3162
- users_dispatcher._wait_between_dispatch = sleep_time
3163
-
3164
- self.assertTrue(users_dispatcher._rebalance)
3165
-
3166
- # Re-balance
3167
- ts = time.perf_counter()
3168
- dispatched_users = next(users_dispatcher)
3169
- delta = time.perf_counter() - ts
3170
- self.assertTrue(0 <= delta <= _TOLERANCE, f"Expected re-balance dispatch to be instantaneous but got {delta}s")
3171
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 3, "User2": 3, "User3": 3})
3172
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 3)
3173
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 3)
3174
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 3)
3175
-
3176
- self.assertFalse(users_dispatcher._rebalance)
3177
-
3178
- # Dispatch iteration 1
3179
- ts = time.perf_counter()
3180
- dispatched_users = next(users_dispatcher)
3181
- delta = time.perf_counter() - ts
3182
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
3183
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 4, "User2": 4, "User3": 4})
3184
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 4)
3185
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 4)
3186
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 4)
3187
-
3188
- # Dispatch iteration 2
3189
- ts = time.perf_counter()
3190
- dispatched_users = next(users_dispatcher)
3191
- delta = time.perf_counter() - ts
3192
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
3193
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 5, "User2": 5, "User3": 5})
3194
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 5)
3195
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 5)
3196
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 5)
3197
-
3198
- # Dispatch iteration 3
3199
- ts = time.perf_counter()
3200
- dispatched_users = next(users_dispatcher)
3201
- delta = time.perf_counter() - ts
3202
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
3203
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 6, "User2": 6, "User3": 6})
3204
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 6)
3205
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 6)
3206
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 6)
3207
-
3208
- def test_add_worker_during_ramp_down(self):
3209
- class User1(User):
3210
- weight = 1
3211
-
3212
- class User2(User):
3213
- weight = 1
3214
-
3215
- class User3(User):
3216
- weight = 1
3217
-
3218
- user_classes = [User1, User2, User3]
3219
-
3220
- worker_nodes = [WorkerNode(str(i + 1)) for i in range(3)]
3221
-
3222
- users_dispatcher = UsersDispatcher(worker_nodes=[worker_nodes[0], worker_nodes[2]], user_classes=user_classes)
3223
-
3224
- users_dispatcher.new_dispatch(target_user_count=18, spawn_rate=3)
3225
- users_dispatcher._wait_between_dispatch = 0
3226
- list(users_dispatcher)
3227
-
3228
- sleep_time = 0.2 # Speed-up test
3229
-
3230
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=3)
3231
- users_dispatcher._wait_between_dispatch = sleep_time
3232
-
3233
- # Dispatch iteration 1
3234
- ts = time.perf_counter()
3235
- dispatched_users = next(users_dispatcher)
3236
- delta = time.perf_counter() - ts
3237
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
3238
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 5, "User2": 5, "User3": 5})
3239
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 8)
3240
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 7)
3241
-
3242
- # Dispatch iteration 2
3243
- ts = time.perf_counter()
3244
- dispatched_users = next(users_dispatcher)
3245
- delta = time.perf_counter() - ts
3246
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
3247
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 4, "User2": 4, "User3": 4})
3248
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 6)
3249
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 6)
3250
-
3251
- self.assertFalse(users_dispatcher._rebalance)
3252
-
3253
- users_dispatcher.add_worker(worker_nodes[1])
3254
-
3255
- self.assertTrue(users_dispatcher._rebalance)
3256
-
3257
- # Re-balance
3258
- ts = time.perf_counter()
3259
- dispatched_users = next(users_dispatcher)
3260
- delta = time.perf_counter() - ts
3261
- self.assertTrue(0 <= delta <= _TOLERANCE, f"Expected re-balance dispatch to be instantaneous but got {delta}s")
3262
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 4, "User2": 4, "User3": 4})
3263
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 4)
3264
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 4)
3265
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 4)
3266
-
3267
- self.assertFalse(users_dispatcher._rebalance)
3268
-
3269
- # Dispatch iteration 3
3270
- ts = time.perf_counter()
3271
- dispatched_users = next(users_dispatcher)
3272
- delta = time.perf_counter() - ts
3273
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
3274
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 3, "User2": 3, "User3": 3})
3275
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 3)
3276
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 3)
3277
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 3)
3278
-
3279
- def test_add_two_workers_during_ramp_down(self):
3280
- class User1(User):
3281
- weight = 1
3282
-
3283
- class User2(User):
3284
- weight = 1
3285
-
3286
- class User3(User):
3287
- weight = 1
3288
-
3289
- user_classes = [User1, User2, User3]
3290
-
3291
- worker_nodes = [WorkerNode(str(i + 1)) for i in range(3)]
3292
-
3293
- users_dispatcher = UsersDispatcher(worker_nodes=[worker_nodes[0]], user_classes=user_classes)
3294
-
3295
- users_dispatcher.new_dispatch(target_user_count=18, spawn_rate=3)
3296
- users_dispatcher._wait_between_dispatch = 0
3297
- list(users_dispatcher)
3298
-
3299
- sleep_time = 0.2 # Speed-up test
3300
-
3301
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=3)
3302
- users_dispatcher._wait_between_dispatch = sleep_time
3303
-
3304
- # Dispatch iteration 1
3305
- ts = time.perf_counter()
3306
- dispatched_users = next(users_dispatcher)
3307
- delta = time.perf_counter() - ts
3308
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
3309
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 5, "User2": 5, "User3": 5})
3310
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 15)
3311
-
3312
- # Dispatch iteration 2
3313
- ts = time.perf_counter()
3314
- dispatched_users = next(users_dispatcher)
3315
- delta = time.perf_counter() - ts
3316
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
3317
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 4, "User2": 4, "User3": 4})
3318
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 12)
3319
-
3320
- self.assertFalse(users_dispatcher._rebalance)
3321
-
3322
- users_dispatcher.add_worker(worker_nodes[1])
3323
- users_dispatcher.add_worker(worker_nodes[2])
3324
-
3325
- self.assertTrue(users_dispatcher._rebalance)
3326
-
3327
- # Re-balance
3328
- ts = time.perf_counter()
3329
- dispatched_users = next(users_dispatcher)
3330
- delta = time.perf_counter() - ts
3331
- self.assertTrue(0 <= delta <= _TOLERANCE, f"Expected re-balance dispatch to be instantaneous but got {delta}s")
3332
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 4, "User2": 4, "User3": 4})
3333
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 4)
3334
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 4)
3335
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 4)
3336
-
3337
- self.assertFalse(users_dispatcher._rebalance)
3338
-
3339
- # Dispatch iteration 3
3340
- ts = time.perf_counter()
3341
- dispatched_users = next(users_dispatcher)
3342
- delta = time.perf_counter() - ts
3343
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
3344
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 3, "User2": 3, "User3": 3})
3345
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 3)
3346
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 3)
3347
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 3)
3348
-
3349
- def test_add_worker_during_ramp_up_with_fixed_user(self):
3350
- class User1(User):
3351
- fixed_count = 2
3352
-
3353
- class User2(User):
3354
- weight = 1
3355
-
3356
- class User3(User):
3357
- weight = 1
3358
-
3359
- user_classes = [User1, User2, User3]
3360
-
3361
- worker_nodes = [
3362
- WorkerNode("hostname1_worker1"),
3363
- WorkerNode("hostname1_worker2"),
3364
- WorkerNode("hostname2_worker1"),
3365
- ]
3366
-
3367
- users_dispatcher = UsersDispatcher(worker_nodes=[worker_nodes[0], worker_nodes[2]], user_classes=user_classes)
3368
-
3369
- sleep_time = 0.2 # Speed-up test
3370
-
3371
- users_dispatcher.new_dispatch(target_user_count=11, spawn_rate=3)
3372
- users_dispatcher._wait_between_dispatch = sleep_time
3373
-
3374
- # Dispatch iteration 1
3375
- ts = time.perf_counter()
3376
- dispatched_users = next(users_dispatcher)
3377
- delta = time.perf_counter() - ts
3378
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
3379
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 1, "User3": 0})
3380
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 2)
3381
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 1)
3382
-
3383
- # Dispatch iteration 2
3384
- ts = time.perf_counter()
3385
- dispatched_users = next(users_dispatcher)
3386
- delta = time.perf_counter() - ts
3387
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
3388
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 2, "User3": 2})
3389
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 3)
3390
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 3)
3391
-
3392
- self.assertFalse(users_dispatcher._rebalance)
3393
-
3394
- users_dispatcher.add_worker(worker_nodes[1])
3395
-
3396
- self.assertTrue(users_dispatcher._rebalance)
3397
-
3398
- # Re-balance
3399
- ts = time.perf_counter()
3400
- dispatched_users = next(users_dispatcher)
3401
- delta = time.perf_counter() - ts
3402
- self.assertTrue(0 <= delta <= _TOLERANCE, f"Expected re-balance dispatch to be instantaneous but got {delta}s")
3403
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 2, "User3": 2})
3404
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 2)
3405
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 2)
3406
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 2)
3407
-
3408
- self.assertFalse(users_dispatcher._rebalance)
3409
-
3410
- # Dispatch iteration 3
3411
- ts = time.perf_counter()
3412
- dispatched_users = next(users_dispatcher)
3413
- delta = time.perf_counter() - ts
3414
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
3415
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 4, "User3": 3})
3416
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 3)
3417
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 3)
3418
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 3)
3419
-
3420
- # Dispatch iteration 4
3421
- ts = time.perf_counter()
3422
- dispatched_users = next(users_dispatcher)
3423
- delta = time.perf_counter() - ts
3424
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
3425
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 2, "User2": 5, "User3": 4})
3426
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 4)
3427
- # without host-based balancing the following two values would be reversed
3428
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 3)
3429
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 4)
3430
-
3431
-
3432
- class TestRampUpUsersFromZeroWithFixed(unittest.TestCase):
3433
- class RampUpCase:
3434
- def __init__(self, fixed_counts: tuple[int], weights: tuple[int], target_user_count: int):
3435
- self.fixed_counts = fixed_counts
3436
- self.weights = weights
3437
- self.target_user_count = target_user_count
3438
-
3439
- def __str__(self):
3440
- return f"<RampUpCase fixed_counts={self.fixed_counts} weights={self.weights} target_user_count={self.target_user_count}>"
3441
-
3442
- def case_handler(self, cases: list[RampUpCase], expected: list[dict[str, int]], user_classes: list[type[User]]):
3443
- self.assertEqual(len(cases), len(expected))
3444
-
3445
- for case_num in range(len(cases)):
3446
- # Reset to default values
3447
- for user_class in user_classes:
3448
- user_class.weight, user_class.fixed_count = 1, 0
3449
-
3450
- case = cases[case_num]
3451
- self.assertEqual(
3452
- len(case.fixed_counts) + len(case.weights),
3453
- len(user_classes),
3454
- msg="Invalid test case or user list.",
3455
- )
3456
-
3457
- fixed_users = user_classes[: len(case.fixed_counts)]
3458
- weighted_users_list = user_classes[len(case.fixed_counts) :]
3459
-
3460
- for user, fixed_count in zip(fixed_users, case.fixed_counts):
3461
- user.fixed_count = fixed_count
3462
-
3463
- for user, weight in zip(weighted_users_list, case.weights):
3464
- user.weight = weight
3465
-
3466
- worker_node1 = WorkerNode("1")
3467
-
3468
- users_dispatcher = UsersDispatcher(worker_nodes=[worker_node1], user_classes=user_classes)
3469
- users_dispatcher.new_dispatch(target_user_count=case.target_user_count, spawn_rate=0.5)
3470
- users_dispatcher._wait_between_dispatch = 0
3471
-
3472
- iterations = list(users_dispatcher)
3473
- self.assertDictEqual(iterations[-1]["1"], expected[case_num], msg=f"Wrong case {case}")
3474
-
3475
- def test_ramp_up_2_weigted_user_with_1_fixed_user(self):
3476
- class User1(User): ...
3477
-
3478
- class User2(User): ...
3479
-
3480
- class User3(User): ...
3481
-
3482
- self.case_handler(
3483
- cases=[
3484
- self.RampUpCase(fixed_counts=(1,), weights=(1, 1), target_user_count=3),
3485
- self.RampUpCase(fixed_counts=(1,), weights=(1, 1), target_user_count=9),
3486
- self.RampUpCase(fixed_counts=(8,), weights=(1, 1), target_user_count=10),
3487
- self.RampUpCase(fixed_counts=(2,), weights=(1, 1), target_user_count=1000),
3488
- self.RampUpCase(fixed_counts=(100,), weights=(1, 1), target_user_count=1000),
3489
- self.RampUpCase(fixed_counts=(960,), weights=(1, 1), target_user_count=1000),
3490
- self.RampUpCase(fixed_counts=(9990,), weights=(1, 1), target_user_count=10000),
3491
- self.RampUpCase(fixed_counts=(100,), weights=(1, 1), target_user_count=100),
3492
- ],
3493
- expected=[
3494
- {"User1": 1, "User2": 1, "User3": 1},
3495
- {"User1": 1, "User2": 4, "User3": 4},
3496
- {"User1": 8, "User2": 1, "User3": 1},
3497
- {"User1": 2, "User2": 499, "User3": 499},
3498
- {"User1": 100, "User2": 450, "User3": 450},
3499
- {"User1": 960, "User2": 20, "User3": 20},
3500
- {"User1": 9990, "User2": 5, "User3": 5},
3501
- {"User1": 100, "User2": 0, "User3": 0},
3502
- ],
3503
- user_classes=[User1, User2, User3],
3504
- )
3505
-
3506
- def test_ramp_up_various_count_weigted_and_fixed_users(self):
3507
- class User1(User): ...
3508
-
3509
- class User2(User): ...
3510
-
3511
- class User3(User): ...
3512
-
3513
- class User4(User): ...
3514
-
3515
- class User5(User): ...
3516
-
3517
- self.case_handler(
3518
- cases=[
3519
- self.RampUpCase(fixed_counts=(), weights=(1, 1, 1, 1, 1), target_user_count=5),
3520
- self.RampUpCase(fixed_counts=(1, 1), weights=(1, 1, 1), target_user_count=5),
3521
- self.RampUpCase(fixed_counts=(5, 2), weights=(1, 1, 1), target_user_count=10),
3522
- self.RampUpCase(fixed_counts=(9, 1), weights=(5, 3, 2), target_user_count=20),
3523
- self.RampUpCase(fixed_counts=(996,), weights=(1, 1, 1, 1), target_user_count=1000),
3524
- self.RampUpCase(fixed_counts=(500,), weights=(2, 1, 1, 1), target_user_count=1000),
3525
- self.RampUpCase(fixed_counts=(250, 250), weights=(3, 1, 1), target_user_count=1000),
3526
- self.RampUpCase(fixed_counts=(1, 1, 1, 1), weights=(100,), target_user_count=1000),
3527
- ],
3528
- expected=[
3529
- {"User1": 1, "User2": 1, "User3": 1, "User4": 1, "User5": 1},
3530
- {"User1": 1, "User2": 1, "User3": 1, "User4": 1, "User5": 1},
3531
- {"User1": 5, "User2": 2, "User3": 1, "User4": 1, "User5": 1},
3532
- {"User1": 9, "User2": 1, "User3": 5, "User4": 3, "User5": 2},
3533
- {"User1": 996, "User2": 1, "User3": 1, "User4": 1, "User5": 1},
3534
- {"User1": 500, "User2": 200, "User3": 100, "User4": 100, "User5": 100},
3535
- {"User1": 250, "User2": 250, "User3": 300, "User4": 100, "User5": 100},
3536
- {"User1": 1, "User2": 1, "User3": 1, "User4": 1, "User5": 996},
3537
- ],
3538
- user_classes=[User1, User2, User3, User4, User5],
3539
- )
3540
-
3541
- def test_ramp_up_only_fixed_users(self):
3542
- class User1(User): ...
3543
-
3544
- class User2(User): ...
3545
-
3546
- class User3(User): ...
3547
-
3548
- class User4(User): ...
3549
-
3550
- class User5(User): ...
3551
-
3552
- self.case_handler(
3553
- cases=[
3554
- self.RampUpCase(fixed_counts=(1, 1, 1, 1, 1), weights=(), target_user_count=5),
3555
- self.RampUpCase(fixed_counts=(13, 26, 39, 52, 1), weights=(), target_user_count=131),
3556
- self.RampUpCase(fixed_counts=(10, 10, 10, 10, 10), weights=(), target_user_count=100),
3557
- self.RampUpCase(fixed_counts=(10, 10, 10, 10, 10), weights=(), target_user_count=50),
3558
- ],
3559
- expected=[
3560
- {"User1": 1, "User2": 1, "User3": 1, "User4": 1, "User5": 1},
3561
- {"User1": 13, "User2": 26, "User3": 39, "User4": 52, "User5": 1},
3562
- {"User1": 10, "User2": 10, "User3": 10, "User4": 10, "User5": 10},
3563
- {"User1": 10, "User2": 10, "User3": 10, "User4": 10, "User5": 10},
3564
- ],
3565
- user_classes=[User1, User2, User3, User4, User5],
3566
- )
3567
-
3568
- def test_ramp_up_partially_ramp_down_and_rump_up_to_target(self):
3569
- class User1(User):
3570
- fixed_count = 50
3571
-
3572
- class User2(User):
3573
- fixed_count = 50
3574
-
3575
- target_count = User1.fixed_count + User2.fixed_count
3576
-
3577
- users_dispatcher = UsersDispatcher(worker_nodes=[WorkerNode("1")], user_classes=[User1, User2])
3578
- users_dispatcher.new_dispatch(target_user_count=30, spawn_rate=0.5)
3579
- users_dispatcher._wait_between_dispatch = 0
3580
- iterations = list(users_dispatcher)
3581
- self.assertDictEqual(iterations[-1]["1"], {"User1": 15, "User2": 15})
3582
-
3583
- users_dispatcher.new_dispatch(target_user_count=20, spawn_rate=0.5)
3584
- users_dispatcher._wait_between_dispatch = 0
3585
- iterations = list(users_dispatcher)
3586
- self.assertDictEqual(iterations[-1]["1"], {"User1": 10, "User2": 10})
3587
-
3588
- users_dispatcher.new_dispatch(target_user_count=target_count, spawn_rate=0.5)
3589
- users_dispatcher._wait_between_dispatch = 0
3590
- iterations = list(users_dispatcher)
3591
- self.assertDictEqual(iterations[-1]["1"], {"User1": 50, "User2": 50})
3592
-
3593
- def test_ramp_up_ramp_down_and_ramp_up_again_single_fixed_class(self):
3594
- class User1(User):
3595
- fixed_count = 2
3596
-
3597
- class User2(User):
3598
- weight = 1
3599
-
3600
- class User3(User):
3601
- weight = 3
3602
-
3603
- user_classes = [User1, User3, User2]
3604
- workers = [WorkerNode("1")]
3605
-
3606
- users_dispatcher = UsersDispatcher(worker_nodes=workers, user_classes=user_classes)
3607
-
3608
- users_dispatcher.new_dispatch(target_user_count=5, spawn_rate=1)
3609
- users_dispatcher._wait_between_dispatch = 0
3610
- iterations = list(users_dispatcher)
3611
- self.assertDictEqual(iterations[-1]["1"], {"User1": 2, "User2": 1, "User3": 2})
3612
-
3613
- users_dispatcher.new_dispatch(target_user_count=2, spawn_rate=1)
3614
- users_dispatcher._wait_between_dispatch = 0
3615
- iterations = list(users_dispatcher)
3616
- self.assertDictEqual(iterations[-1]["1"], {"User1": 2, "User2": 0, "User3": 0})
3617
-
3618
- users_dispatcher.new_dispatch(target_user_count=7, spawn_rate=1)
3619
- users_dispatcher._wait_between_dispatch = 0
3620
- iterations = list(users_dispatcher)
3621
- self.assertDictEqual(iterations[-1]["1"], {"User1": 2, "User2": 1, "User3": 4})
3622
-
3623
- def test_ramp_up_ramp_down_and_ramp_up_again(self):
3624
- for weights, fixed_counts in [
3625
- [(1, 1, 1, 1, 1), (100, 100, 50, 50, 200)],
3626
- [(1, 1, 1, 1, 1), (100, 150, 50, 50, 0)],
3627
- [(1, 1, 1, 1, 1), (200, 100, 50, 0, 0)],
3628
- [(1, 1, 1, 1, 1), (200, 100, 0, 0, 0)],
3629
- [(1, 1, 1, 1, 1), (200, 0, 0, 0, 0)],
3630
- [(1, 1, 1, 1, 1), (0, 0, 0, 0, 0)],
3631
- ]:
3632
- u1_weight, u2_weight, u3_weight, u4_weight, u5_weight = weights
3633
- u1_fixed_count, u2_fixed_count, u3_fixed_count, u4_fixed_count, u5_fixed_count = fixed_counts
3634
-
3635
- class User1(User):
3636
- weight = u1_weight
3637
- fixed_count = u1_fixed_count
3638
-
3639
- class User2(User):
3640
- weight = u2_weight
3641
- fixed_count = u2_fixed_count
3642
-
3643
- class User3(User):
3644
- weight = u3_weight
3645
- fixed_count = u3_fixed_count
3646
-
3647
- class User4(User):
3648
- weight = u4_weight
3649
- fixed_count = u4_fixed_count
3650
-
3651
- class User5(User):
3652
- weight = u5_weight
3653
- fixed_count = u5_fixed_count
3654
-
3655
- target_user_counts = [sum(fixed_counts), sum(fixed_counts) + 100]
3656
- down_counts = [0, max(min(fixed_counts) - 1, 0)]
3657
- user_classes = [User1, User2, User3, User4, User5]
3658
-
3659
- for worker_count in [3, 5, 9]:
3660
- workers = [WorkerNode(str(i + 1)) for i in range(worker_count)]
3661
- users_dispatcher = UsersDispatcher(worker_nodes=workers, user_classes=user_classes)
3662
-
3663
- for down_to_count in down_counts:
3664
- for target_user_count in target_user_counts:
3665
- # Ramp-up to go to `target_user_count` #########
3666
-
3667
- users_dispatcher.new_dispatch(target_user_count=target_user_count, spawn_rate=1)
3668
- users_dispatcher._wait_between_dispatch = 0
3669
-
3670
- list(users_dispatcher)
3671
-
3672
- for user_class in user_classes:
3673
- if user_class.fixed_count:
3674
- self.assertEqual(
3675
- users_dispatcher._get_user_current_count(user_class.__name__),
3676
- user_class.fixed_count,
3677
- msg=f"{user_class.__name__}, {target_user_count}",
3678
- )
3679
-
3680
- # Ramp-down to go to `down_to_count`
3681
- # and ensure the fixed users was decreased too
3682
-
3683
- users_dispatcher.new_dispatch(target_user_count=down_to_count, spawn_rate=1)
3684
- users_dispatcher._wait_between_dispatch = 0
3685
-
3686
- list(users_dispatcher)
3687
-
3688
- for user_class in user_classes:
3689
- if user_class.fixed_count:
3690
- self.assertNotEqual(
3691
- users_dispatcher._get_user_current_count(user_class.__name__),
3692
- user_class.fixed_count,
3693
- )
3694
-
3695
- # Ramp-up go back to `target_user_count` and ensure
3696
- # the fixed users return to their counts
3697
-
3698
- users_dispatcher.new_dispatch(target_user_count=target_user_count, spawn_rate=1)
3699
- users_dispatcher._wait_between_dispatch = 0
3700
-
3701
- list(users_dispatcher)
3702
-
3703
- for user_class in user_classes:
3704
- if user_class.fixed_count:
3705
- self.assertEqual(
3706
- users_dispatcher._get_user_current_count(user_class.__name__),
3707
- user_class.fixed_count,
3708
- )
3709
-
3710
-
3711
- class TestRampUpDifferentUsers(unittest.TestCase):
3712
- def test_ramp_up_different_users_for_each_dispatch(self):
3713
- class User1(User):
3714
- weight = 1
3715
-
3716
- class User2(User):
3717
- weight = 1
3718
-
3719
- class User3(User):
3720
- weight = 1
3721
-
3722
- worker_node1 = WorkerNode("1")
3723
-
3724
- user_dispatcher = UsersDispatcher(worker_nodes=[worker_node1], user_classes=[User1, User2, User3])
3725
-
3726
- user_dispatcher.new_dispatch(target_user_count=3, spawn_rate=3)
3727
- self.assertDictEqual(next(user_dispatcher), {"1": {"User1": 1, "User2": 1, "User3": 1}})
3728
- user_dispatcher.new_dispatch(target_user_count=4, spawn_rate=1, user_classes=[User1])
3729
- self.assertDictEqual(next(user_dispatcher), {"1": {"User1": 2, "User2": 1, "User3": 1}})
3730
-
3731
- user_dispatcher.new_dispatch(target_user_count=5, spawn_rate=1, user_classes=[User2])
3732
- self.assertDictEqual(next(user_dispatcher), {"1": {"User1": 2, "User2": 2, "User3": 1}})
3733
-
3734
- user_dispatcher.new_dispatch(target_user_count=6, spawn_rate=1, user_classes=[User3])
3735
- self.assertDictEqual(next(user_dispatcher), {"1": {"User1": 2, "User2": 2, "User3": 2}})
3736
-
3737
- def test_ramp_up_only_one_kind_of_user(self):
3738
- class User1(User):
3739
- weight = 1
3740
-
3741
- class User2(User):
3742
- weight = 1
3743
-
3744
- class User3(User):
3745
- weight = 1
3746
-
3747
- worker_node1 = WorkerNode("1")
3748
-
3749
- user_dispatcher = UsersDispatcher(worker_nodes=[worker_node1], user_classes=[User1, User2, User3])
3750
-
3751
- user_dispatcher.new_dispatch(target_user_count=10, spawn_rate=10, user_classes=[User2])
3752
- self.assertDictEqual(next(user_dispatcher), {"1": {"User1": 0, "User2": 10, "User3": 0}})
3753
-
3754
- def test_ramp_up_first_half_user1_second_half_user2(self):
3755
- class User1(User):
3756
- weight = 1
3757
-
3758
- class User2(User):
3759
- weight = 1
3760
-
3761
- class User3(User):
3762
- weight = 1
3763
-
3764
- worker_node1 = WorkerNode("1")
3765
-
3766
- user_dispatcher = UsersDispatcher(worker_nodes=[worker_node1], user_classes=[User1, User2, User3])
3767
-
3768
- user_dispatcher.new_dispatch(target_user_count=10, spawn_rate=10, user_classes=[User2])
3769
- self.assertDictEqual(next(user_dispatcher), {"1": {"User1": 0, "User2": 10, "User3": 0}})
3770
-
3771
- user_dispatcher.new_dispatch(target_user_count=40, spawn_rate=30, user_classes=[User3])
3772
- self.assertDictEqual(next(user_dispatcher), {"1": {"User1": 0, "User2": 10, "User3": 30}})
3773
-
3774
- def test_ramp_up_first_one_user_then_all_classes(self):
3775
- class User1(User):
3776
- weight = 1
3777
-
3778
- class User2(User):
3779
- weight = 1
3780
-
3781
- class User3(User):
3782
- weight = 1
3783
-
3784
- worker_node1 = WorkerNode("1")
3785
-
3786
- user_dispatcher = UsersDispatcher(worker_nodes=[worker_node1], user_classes=[User1, User2, User3])
3787
-
3788
- user_dispatcher.new_dispatch(target_user_count=10, spawn_rate=10, user_classes=[User2])
3789
- self.assertDictEqual(next(user_dispatcher), {"1": {"User1": 0, "User2": 10, "User3": 0}})
3790
-
3791
- user_dispatcher.new_dispatch(target_user_count=40, spawn_rate=30, user_classes=[User1, User2, User3])
3792
- self.assertDictEqual(next(user_dispatcher), {"1": {"User1": 10, "User2": 20, "User3": 10}})
3793
-
3794
- def test_ramp_up_different_users_each_dispatch_multiple_worker(self):
3795
- class User1(User):
3796
- weight = 1
3797
-
3798
- class User2(User):
3799
- weight = 1
3800
-
3801
- class User3(User):
3802
- weight = 1
3803
-
3804
- worker_node1 = WorkerNode("1")
3805
- worker_node2 = WorkerNode("2")
3806
- worker_node3 = WorkerNode("3")
3807
-
3808
- user_dispatcher = UsersDispatcher(
3809
- worker_nodes=[worker_node1, worker_node2, worker_node3], user_classes=[User1, User2, User3]
3810
- )
3811
-
3812
- user_dispatcher.new_dispatch(target_user_count=9, spawn_rate=9)
3813
- self.assertDictEqual(
3814
- next(user_dispatcher),
3815
- {
3816
- "1": {"User1": 3, "User2": 0, "User3": 0},
3817
- "2": {"User1": 0, "User2": 3, "User3": 0},
3818
- "3": {"User1": 0, "User2": 0, "User3": 3},
3819
- },
3820
- )
3821
-
3822
- user_dispatcher.new_dispatch(target_user_count=12, spawn_rate=3, user_classes=[User3])
3823
- self.assertDictEqual(
3824
- next(user_dispatcher),
3825
- {
3826
- "1": {"User1": 3, "User2": 0, "User3": 1},
3827
- "2": {"User1": 0, "User2": 3, "User3": 1},
3828
- "3": {"User1": 0, "User2": 0, "User3": 4},
3829
- },
3830
- )
3831
-
3832
- user_dispatcher.new_dispatch(target_user_count=15, spawn_rate=3, user_classes=[User2])
3833
- self.assertDictEqual(
3834
- next(user_dispatcher),
3835
- {
3836
- "1": {"User1": 3, "User2": 1, "User3": 1},
3837
- "2": {"User1": 0, "User2": 4, "User3": 1},
3838
- "3": {"User1": 0, "User2": 1, "User3": 4},
3839
- },
3840
- )
3841
-
3842
- user_dispatcher.new_dispatch(target_user_count=18, spawn_rate=3, user_classes=[User1])
3843
- self.assertDictEqual(
3844
- next(user_dispatcher),
3845
- {
3846
- "1": {"User1": 4, "User2": 1, "User3": 1},
3847
- "2": {"User1": 1, "User2": 4, "User3": 1},
3848
- "3": {"User1": 1, "User2": 1, "User3": 4},
3849
- },
3850
- )
3851
-
3852
- def test_ramp_up_one_user_class_multiple_worker(self):
3853
- class User1(User):
3854
- weight = 1
3855
-
3856
- class User2(User):
3857
- weight = 1
3858
-
3859
- class User3(User):
3860
- weight = 1
3861
-
3862
- worker_node1 = WorkerNode("1")
3863
- worker_node2 = WorkerNode("2")
3864
- worker_node3 = WorkerNode("3")
3865
-
3866
- user_dispatcher = UsersDispatcher(
3867
- worker_nodes=[worker_node1, worker_node2, worker_node3], user_classes=[User1, User2, User3]
3868
- )
3869
-
3870
- user_dispatcher.new_dispatch(target_user_count=60, spawn_rate=60, user_classes=[User2])
3871
- self.assertDictEqual(
3872
- next(user_dispatcher),
3873
- {
3874
- "1": {"User1": 0, "User2": 20, "User3": 0},
3875
- "2": {"User1": 0, "User2": 20, "User3": 0},
3876
- "3": {"User1": 0, "User2": 20, "User3": 0},
3877
- },
3878
- )
3879
-
3880
- def test_ramp_down_custom_user_classes_respect_weighting(self):
3881
- class User1(User):
3882
- weight = 1
3883
-
3884
- class User2(User):
3885
- weight = 1
3886
-
3887
- class User3(User):
3888
- weight = 1
3889
-
3890
- worker_nodes = [WorkerNode(str(i + 1)) for i in range(3)]
3891
- user_dispatcher = UsersDispatcher(worker_nodes=worker_nodes, user_classes=[User1, User2, User3])
3892
-
3893
- user_dispatcher.new_dispatch(target_user_count=20, spawn_rate=20, user_classes=[User3])
3894
- dispatched_users = next(user_dispatcher)
3895
- self.assertDictEqual(
3896
- dispatched_users,
3897
- {
3898
- "1": {"User1": 0, "User2": 0, "User3": 7},
3899
- "2": {"User1": 0, "User2": 0, "User3": 7},
3900
- "3": {"User1": 0, "User2": 0, "User3": 6},
3901
- },
3902
- )
3903
-
3904
- user_dispatcher.new_dispatch(target_user_count=9, spawn_rate=20, user_classes=[User3])
3905
- dispatched_users = next(user_dispatcher)
3906
- self.assertDictEqual(
3907
- dispatched_users,
3908
- {
3909
- "1": {"User1": 0, "User2": 0, "User3": 3},
3910
- "2": {"User1": 0, "User2": 0, "User3": 3},
3911
- "3": {"User1": 0, "User2": 0, "User3": 3},
3912
- },
3913
- )
3914
-
3915
- user_dispatcher.new_dispatch(target_user_count=3, spawn_rate=20, user_classes=[User1, User2, User3])
3916
- dispatched_users = next(user_dispatcher)
3917
- self.assertDictEqual(
3918
- dispatched_users,
3919
- {
3920
- "1": {"User1": 0, "User2": 0, "User3": 1},
3921
- "2": {"User1": 0, "User2": 0, "User3": 1},
3922
- "3": {"User1": 0, "User2": 0, "User3": 1},
3923
- },
3924
- )
3925
-
3926
- user_dispatcher.new_dispatch(target_user_count=21, spawn_rate=21, user_classes=[User1, User2, User3])
3927
- dispatched_users = next(user_dispatcher)
3928
- self.assertDictEqual(
3929
- dispatched_users,
3930
- {
3931
- "1": {"User1": 0, "User2": 6, "User3": 1}, # 7
3932
- "2": {"User1": 0, "User2": 0, "User3": 7}, # 7
3933
- "3": {"User1": 6, "User2": 0, "User3": 1}, # 7
3934
- },
3935
- )
3936
-
3937
- user_dispatcher.new_dispatch(target_user_count=9, spawn_rate=20, user_classes=[User1, User2, User3])
3938
- dispatched_users = next(user_dispatcher)
3939
-
3940
- # this is disrespecting the weighting
3941
-
3942
- self.assertDictEqual(
3943
- dispatched_users,
3944
- {
3945
- "1": {"User1": 0, "User2": 2, "User3": 1},
3946
- "2": {"User1": 0, "User2": 0, "User3": 3},
3947
- "3": {"User1": 2, "User2": 0, "User3": 1},
3948
- },
3949
- )
3950
-
3951
- def test_remove_worker_during_ramp_up_custom_classes(self):
3952
- class User1(User):
3953
- weight = 1
3954
-
3955
- class User2(User):
3956
- weight = 1
3957
-
3958
- class User3(User):
3959
- weight = 1
3960
-
3961
- user_classes = [User1, User2, User3]
3962
-
3963
- worker_nodes = [WorkerNode(str(i + 1)) for i in range(3)]
3964
-
3965
- users_dispatcher = UsersDispatcher(worker_nodes=worker_nodes, user_classes=user_classes)
3966
-
3967
- sleep_time = 0.2 # Speed-up test
3968
-
3969
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=3, user_classes=[User2])
3970
- users_dispatcher._wait_between_dispatch = sleep_time
3971
-
3972
- # Dispatch iteration 1
3973
- ts = time.perf_counter()
3974
- dispatched_users = next(users_dispatcher)
3975
- delta = time.perf_counter() - ts
3976
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
3977
- self.assertDictEqual(
3978
- dispatched_users,
3979
- {
3980
- "1": {"User1": 0, "User2": 1, "User3": 0},
3981
- "2": {"User1": 0, "User2": 1, "User3": 0},
3982
- "3": {"User1": 0, "User2": 1, "User3": 0},
3983
- },
3984
- )
3985
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 0, "User2": 3, "User3": 0})
3986
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 1)
3987
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 1)
3988
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 1)
3989
-
3990
- # Dispatch iteration 2
3991
- ts = time.perf_counter()
3992
- dispatched_users = next(users_dispatcher)
3993
- delta = time.perf_counter() - ts
3994
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
3995
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 0, "User2": 6, "User3": 0})
3996
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 2)
3997
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 2)
3998
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 2)
3999
-
4000
- self.assertFalse(users_dispatcher._rebalance)
4001
-
4002
- users_dispatcher.remove_worker(worker_nodes[1])
4003
-
4004
- self.assertTrue(users_dispatcher._rebalance)
4005
-
4006
- # Re-balance
4007
- ts = time.perf_counter()
4008
- dispatched_users = next(users_dispatcher)
4009
- delta = time.perf_counter() - ts
4010
- self.assertTrue(0 <= delta <= _TOLERANCE, f"Expected re-balance dispatch to be instantaneous but got {delta}s")
4011
- self.assertDictEqual(
4012
- dispatched_users, {"1": {"User1": 0, "User2": 3, "User3": 0}, "3": {"User1": 0, "User2": 3, "User3": 0}}
4013
- )
4014
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 0, "User2": 6, "User3": 0})
4015
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 3)
4016
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 3)
4017
-
4018
- self.assertFalse(users_dispatcher._rebalance)
4019
-
4020
- # Dispatch iteration 3
4021
- ts = time.perf_counter()
4022
- dispatched_users = next(users_dispatcher)
4023
- delta = time.perf_counter() - ts
4024
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
4025
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 0, "User2": 9, "User3": 0})
4026
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 5)
4027
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 4)
4028
-
4029
- # New dispatch
4030
- users_dispatcher.new_dispatch(16, 7, [User3])
4031
- dispatched_users = next(users_dispatcher)
4032
- self.assertDictEqual(
4033
- dispatched_users, {"1": {"User1": 0, "User2": 5, "User3": 3}, "3": {"User1": 0, "User2": 4, "User3": 4}}
4034
- )
4035
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 0, "User2": 9, "User3": 7})
4036
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 8)
4037
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 8)
4038
-
4039
- def test_add_worker_during_ramp_up_custom_classes(self):
4040
- class User1(User):
4041
- weight = 1
4042
-
4043
- class User2(User):
4044
- weight = 1
4045
-
4046
- class User3(User):
4047
- weight = 1
4048
-
4049
- user_classes = [User1, User2, User3]
4050
-
4051
- worker_nodes = [WorkerNode(str(i + 1)) for i in range(3)]
4052
-
4053
- users_dispatcher = UsersDispatcher(worker_nodes=[worker_nodes[0], worker_nodes[2]], user_classes=user_classes)
4054
-
4055
- sleep_time = 0.2 # Speed-up test
4056
-
4057
- users_dispatcher.new_dispatch(target_user_count=11, spawn_rate=3, user_classes=[User1])
4058
- users_dispatcher._wait_between_dispatch = sleep_time
4059
-
4060
- # Dispatch iteration 1
4061
- ts = time.perf_counter()
4062
- dispatched_users = next(users_dispatcher)
4063
- delta = time.perf_counter() - ts
4064
- self.assertTrue(0 <= delta <= _TOLERANCE, delta)
4065
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 3, "User2": 0, "User3": 0})
4066
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 2)
4067
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 1)
4068
-
4069
- # Dispatch iteration 2
4070
- ts = time.perf_counter()
4071
- dispatched_users = next(users_dispatcher)
4072
- delta = time.perf_counter() - ts
4073
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
4074
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 6, "User2": 0, "User3": 0})
4075
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 3)
4076
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 3)
4077
-
4078
- self.assertFalse(users_dispatcher._rebalance)
4079
-
4080
- users_dispatcher.add_worker(worker_nodes[1])
4081
-
4082
- self.assertTrue(users_dispatcher._rebalance)
4083
-
4084
- # Re-balance
4085
- ts = time.perf_counter()
4086
- dispatched_users = next(users_dispatcher)
4087
- delta = time.perf_counter() - ts
4088
- self.assertTrue(0 <= delta <= _TOLERANCE, f"Expected re-balance dispatch to be instantaneous but got {delta}s")
4089
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 6, "User2": 0, "User3": 0})
4090
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 2)
4091
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 2)
4092
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 2)
4093
-
4094
- self.assertFalse(users_dispatcher._rebalance)
4095
-
4096
- # Dispatch iteration 3
4097
- ts = time.perf_counter()
4098
- dispatched_users = next(users_dispatcher)
4099
- delta = time.perf_counter() - ts
4100
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
4101
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 9, "User2": 0, "User3": 0})
4102
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 3)
4103
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 3)
4104
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 3)
4105
-
4106
- # Dispatch iteration 4
4107
- ts = time.perf_counter()
4108
- dispatched_users = next(users_dispatcher)
4109
- delta = time.perf_counter() - ts
4110
- self.assertTrue(sleep_time - _TOLERANCE <= delta <= sleep_time + _TOLERANCE, delta)
4111
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 11, "User2": 0, "User3": 0})
4112
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 4)
4113
- # without host-based balancing the following two values would be reversed
4114
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 4)
4115
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 3)
4116
-
4117
- # New Dispatch
4118
- users_dispatcher.new_dispatch(target_user_count=18, spawn_rate=7, user_classes=[User3])
4119
- dispatched_users = next(users_dispatcher)
4120
- self.assertDictEqual(_aggregate_dispatched_users(dispatched_users), {"User1": 11, "User2": 0, "User3": 7})
4121
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[0].id), 6)
4122
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[1].id), 6)
4123
- self.assertEqual(_user_count_on_worker(dispatched_users, worker_nodes[2].id), 6)
4124
-
4125
-
4126
- class TestFloatWeithts(unittest.TestCase):
4127
- def test_float_weights(self):
4128
- """Final distribution should be {"User1": 3, "User2": 3, "User3": 3}"""
4129
-
4130
- for ratio in (1, 1.0, 10, 2.5, 0.3, 1 / 23, math.e, math.pi):
4131
-
4132
- class User1(User):
4133
- weight = 1 * ratio
4134
-
4135
- class User2(User):
4136
- weight = 2 * ratio
4137
-
4138
- class User3(User):
4139
- weight = 3 * ratio
4140
-
4141
- worker_node1 = WorkerNode("1")
4142
- worker_node2 = WorkerNode("2")
4143
- worker_node3 = WorkerNode("3")
4144
-
4145
- sleep_time = 0 # Speed-up test
4146
-
4147
- users_dispatcher = UsersDispatcher(
4148
- worker_nodes=[worker_node1, worker_node2, worker_node3], user_classes=[User1, User2, User3]
4149
- )
4150
- users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=0.5)
4151
- users_dispatcher._wait_between_dispatch = sleep_time
4152
-
4153
- if ratio == 1:
4154
- reference = list(users_dispatcher)
4155
- else:
4156
- for x in reference:
4157
- self.assertDictEqual(x, next(users_dispatcher))
4158
-
4159
-
4160
- def _aggregate_dispatched_users(d: dict[str, dict[str, int]]) -> dict[str, int]:
4161
- user_classes = list(next(iter(d.values())).keys())
4162
- return {u: sum(d[u] for d in d.values()) for u in user_classes}
4163
-
4164
-
4165
- def _user_count(d: dict[str, dict[str, int]]) -> int:
4166
- return sum(map(sum, map(dict.values, d.values())))
4167
-
4168
-
4169
- def _user_count_on_worker(d: dict[str, dict[str, int]], worker_node_id: str) -> int:
4170
- return sum(d[worker_node_id].values())