gru 0.1.1 → 0.1.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 89ae1e251f9db9637786d27b5abeb7de7fd0bef7
4
- data.tar.gz: 0dbd72c564a5622ec09abd483d408c7c851e072b
3
+ metadata.gz: c8569b8dac503cf08983a463bb293a277f09f301
4
+ data.tar.gz: 45da184cdbe70af5c0f871d65a9d4f60de2555f6
5
5
  SHA512:
6
- metadata.gz: b3740b2ee29bc4f65968eedd061d5f6fb506a24152088329e7297ecc519b67f8f1f044b03fe515a185987a09691b7b0bf564a9fa0cc2801adca8072df8fd2796
7
- data.tar.gz: f360078d5e374287902e5ce48d78cd6015dd7a83bf986b993ad5a771f2dda556cc64335848f48f81e2dbfe800b3f15f2af04255b013b5e9ff5efc30e4774384c
6
+ metadata.gz: ad6bc8c502243e5f9f8ce86dfe6275758f4eff9ad55f562d070ad99484782f58d1bb49730413d3314e42c5dd92b8fb6f4950a9c290fb97d1168692fd168a264a
7
+ data.tar.gz: 21d48ee74d9fc7bda158b7f2485bbb97ba35b6e9401bbc2a306070a802262f8ae7a78c65df7fdb0d552d6e013cdffcedd95989cd61719e59b10c31cdab60fea8
@@ -190,24 +190,24 @@ module Gru
190
190
  host_running,global_running,host_max,global_max = worker_counts(worker)
191
191
  result = false
192
192
  if rebalance_cluster?
193
- result = host_running.to_i < max_workers_per_host(global_max,host_max) &&
194
- host_running.to_i < @settings.max_worker_processes_per_host
193
+ result = host_running.to_i < max_workers_per_host(global_max,host_max)
195
194
  else
196
195
  result = host_running.to_i < host_max.to_i
197
196
  end
198
- result && global_running.to_i < global_max.to_i
197
+ result && global_running.to_i < global_max.to_i &&
198
+ total_host_running < @settings.max_worker_processes_per_host
199
199
  end
200
200
 
201
201
  def expire_worker?(worker)
202
202
  host_running,global_running,host_max,global_max = worker_counts(worker)
203
203
  result = false
204
204
  if rebalance_cluster?
205
- result = host_running.to_i > max_workers_per_host(global_max,host_max) ||
206
- host_running.to_i > @settings.max_worker_processes_per_host
205
+ result = host_running.to_i > max_workers_per_host(global_max,host_max)
207
206
  else
208
207
  result = host_running.to_i > host_max.to_i
209
208
  end
210
- (result || global_running.to_i > global_max.to_i) && host_running.to_i >= 0
209
+ (result || global_running.to_i > global_max.to_i) && host_running.to_i >= 0 ||
210
+ total_host_running > @settings.max_worker_processes_per_host
211
211
  end
212
212
 
213
213
  def worker_counts(worker)
@@ -232,6 +232,10 @@ module Gru
232
232
  send_message(:hget,host_workers_running_key,worker).to_i
233
233
  end
234
234
 
235
+ def total_host_running
236
+ send_message(:hgetall,host_workers_running_key).values.reduce(0) {|sum, value| sum + value.to_i }
237
+ end
238
+
235
239
  def gru_host_count
236
240
  send_message(:keys,"#{gru_key}:*:workers_running").count - 1
237
241
  end
@@ -1,3 +1,3 @@
1
1
  module Gru
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -84,6 +84,7 @@ describe Gru::Adapters::RedisAdapter do
84
84
  expect(client).to receive(:setnx).exactly(3).times.and_return(true)
85
85
  expect(client).to receive(:del).with("#{gru_key}:test_worker").exactly(3).times
86
86
  expect(client).to receive(:get).with("#{gru_key}:rebalance").exactly(3).times
87
+ expect(client).to receive(:hgetall).with("#{gru_key}:#{hostname}:workers_running").exactly(3).times.and_return({'test_worker' => '1'})
87
88
  expect(client).to receive(:hincrby).with("#{gru_key}:global:workers_running",'test_worker',1).exactly(3).times
88
89
  expect(client).to receive(:hincrby).with("#{gru_key}:#{hostname}:workers_running",'test_worker',1).exactly(3).times
89
90
  expect(client).to receive(:hget).with("#{gru_key}:#{hostname}:max_workers",'test_worker').exactly(2).times
@@ -99,6 +100,7 @@ describe Gru::Adapters::RedisAdapter do
99
100
  expect(client).to receive(:setnx).exactly(3).times.and_return(true)
100
101
  expect(client).to receive(:del).with("#{gru_key}:test_worker").exactly(3).times
101
102
  expect(client).to receive(:get).with("#{gru_key}:rebalance").exactly(3).times
103
+ expect(client).to receive(:hgetall).with("#{gru_key}:#{hostname}:workers_running").exactly(3).times.and_return({'test_worker' => '1'})
102
104
  expect(client).to receive(:hincrby).with("#{gru_key}:global:workers_running",'test_worker',1).exactly(3).times
103
105
  expect(client).to receive(:hincrby).with("#{gru_key}:#{hostname}:workers_running",'test_worker',1).exactly(3).times
104
106
  expect(client).to receive(:hget).with("#{gru_key}:#{hostname}:max_workers",'test_worker').exactly(2).times
@@ -189,11 +191,13 @@ describe Gru::Adapters::RedisAdapter do
189
191
 
190
192
  it "doesn't remove workers when local maximum has not been exceeded" do
191
193
  expect(client).to receive(:multi).exactly(1).times.and_yield(client).and_return([3,3,4,3])
194
+ expect(client).to receive(:hgetall).with("#{gru_key}:#{hostname}:workers_running").exactly(1).times.and_return({'test_worker' => '1'})
192
195
  expect(adapter.expire_workers).to eq({'test_worker' => 0})
193
196
  end
194
197
 
195
198
  it "doesn't remove workers when global maximum has not been exceeded" do
196
199
  expect(client).to receive(:multi).exactly(1).times.and_yield(client).and_return([3,4,3,5])
200
+ expect(client).to receive(:hgetall).with("#{gru_key}:#{hostname}:workers_running").exactly(1).times.and_return({'test_worker' => '1'})
197
201
  expect(adapter.expire_workers).to eq({'test_worker' => 0})
198
202
  end
199
203
  end
@@ -220,6 +224,7 @@ describe Gru::Adapters::RedisAdapter do
220
224
  expect(client).to receive(:hget).with("#{gru_key}:#{hostname}:max_workers",'test_worker').exactly(3).times
221
225
  expect(client).to receive(:hget).with("#{gru_key}:global:max_workers",'test_worker').exactly(3).times
222
226
  expect(client).to receive(:keys).with("#{gru_key}:*:workers_running").exactly(3).times.and_return(['foo'])
227
+ expect(client).to receive(:hgetall).with("#{gru_key}:#{hostname}:workers_running").exactly(3).times.and_return({'test_worker' => '1'})
223
228
  expect(client).to receive(:setnx).exactly(3).times.and_return(true)
224
229
  expect(client).to receive(:hincrby).with("#{gru_key}:global:workers_running",'test_worker',1).exactly(3).times
225
230
  expect(client).to receive(:hincrby).with("#{gru_key}:#{hostname}:workers_running",'test_worker',1).exactly(3).times
@@ -233,6 +238,7 @@ describe Gru::Adapters::RedisAdapter do
233
238
  expect(client).to receive(:hgetall).with("#{gru_key}:#{hostname}:max_workers").and_return(config.cluster_maximums)
234
239
  expect(client).to receive(:keys).with("#{gru_key}:*:workers_running").exactly(3).times.and_return(["test1","test2"])
235
240
  expect(client).to receive(:get).with("#{gru_key}:rebalance").exactly(3).times.and_return("true")
241
+ expect(client).to receive(:hgetall).with("#{gru_key}:#{hostname}:workers_running").exactly(3).times.and_return({'test_worker' => '10'})
236
242
  expect(client).to receive(:hget).with("#{gru_key}:#{hostname}:max_workers",'test_worker').exactly(3).times
237
243
  expect(client).to receive(:hget).with("#{gru_key}:global:max_workers",'test_worker').exactly(3).times
238
244
  expect(client).to receive(:hget).with("#{gru_key}:#{hostname}:workers_running",'test_worker').exactly(3).times
@@ -245,6 +251,8 @@ describe Gru::Adapters::RedisAdapter do
245
251
  expect(client).to receive(:multi).exactly(3).times.and_yield(client).and_return([9,20,20,30], [10,20,20,30])
246
252
  expect(client).to receive(:hgetall).with("#{gru_key}:#{hostname}:max_workers").and_return(config.cluster_maximums)
247
253
  expect(client).to receive(:setnx).exactly(1).times.and_return(true)
254
+ expect(client).to receive(:hgetall).with("#{gru_key}:#{hostname}:workers_running").exactly(3).times.and_return(
255
+ {'test_worker' => '9'}, {'test_worker' => '10'}, {'test_worker' => '10'})
248
256
  expect(client).to receive(:hincrby).with("#{gru_key}:foo:workers_running",'test_worker',1).exactly(1).times
249
257
  expect(client).to receive(:hincrby).with("#{gru_key}:global:workers_running",'test_worker',1).exactly(1).times
250
258
  expect(client).to receive(:del).with("#{gru_key}:test_worker").exactly(1).times
@@ -8,7 +8,6 @@ require 'pry'
8
8
  # This requires a redis server instance running on localhost
9
9
  # un-pend to run with actual redis-server
10
10
  xdescribe Gru do
11
-
12
11
  after {
13
12
  client.flushdb
14
13
  }
@@ -167,5 +166,323 @@ xdescribe Gru do
167
166
  test_client.hset("GRU:default:default:heartbeats", 'test3', Time.now - 300)
168
167
  expect(test2.adjust_workers).to eq( { 'test_worker' => 3 })
169
168
  end
169
+
170
+ end
171
+
172
+ context "max workers per host with high maximum worker count" do
173
+ let(:settings) {
174
+ {
175
+ rebalance_flag: true,
176
+ manage_worker_heartbeats: true,
177
+ max_workers_per_host: nil,
178
+ cluster_maximums: {
179
+ 'test_worker' => '100'
180
+ }
181
+ }
182
+ }
183
+
184
+ let(:test_client) {
185
+ Redis.new
186
+ }
187
+
188
+ let(:adapter1) {
189
+ adapter1 = Gru::Adapters::RedisAdapter.new(Gru::Configuration.new(settings.clone))
190
+ allow(adapter1).to receive(:hostname).and_return('test1')
191
+ adapter1
192
+ }
193
+
194
+ let(:adapter2) {
195
+ adapter2 = Gru::Adapters::RedisAdapter.new(Gru::Configuration.new(settings.clone))
196
+ allow(adapter2).to receive(:hostname).and_return('test2')
197
+ adapter2
198
+ }
199
+
200
+ let(:adapter3) {
201
+ adapter3 = Gru::Adapters::RedisAdapter.new(Gru::Configuration.new(settings.clone))
202
+ allow(adapter3).to receive(:hostname).and_return('test3')
203
+ adapter3
204
+ }
205
+
206
+ let(:adapter4) {
207
+ adapter4 = Gru::Adapters::RedisAdapter.new(Gru::Configuration.new(settings.clone))
208
+ allow(adapter4).to receive(:hostname).and_return('test4')
209
+ adapter4
210
+ }
211
+
212
+ it "doesn't exceed max processes per host setting" do
213
+ test1 = Gru::WorkerManager.new(adapter1)
214
+ test2 = Gru::WorkerManager.new(adapter2)
215
+ test3 = Gru::WorkerManager.new(adapter3)
216
+ test1.register_workers
217
+ test2.register_workers
218
+ test3.register_workers
219
+ expect(test1.adjust_workers).to eq( { 'test_worker' => 30 })
220
+ expect(test2.adjust_workers).to eq( { 'test_worker' => 30 })
221
+ expect(test3.adjust_workers).to eq( { 'test_worker' => 30 })
222
+ end
223
+
224
+ it "honors the max processes per host setting and correctly rebalances worker counts" do
225
+ test1 = Gru::WorkerManager.new(adapter1)
226
+ test2 = Gru::WorkerManager.new(adapter2)
227
+ test3 = Gru::WorkerManager.new(adapter3)
228
+ test4 = Gru::WorkerManager.new(adapter4)
229
+ test1.register_workers
230
+ test2.register_workers
231
+ test3.register_workers
232
+ # Honors max processes per host setting
233
+ expect(test1.adjust_workers).to eq({ 'test_worker' => 30 })
234
+ expect(test2.adjust_workers).to eq({ 'test_worker' => 30 })
235
+ expect(test3.adjust_workers).to eq({ 'test_worker' => 30 })
236
+ test4.register_workers
237
+ # Pick up remaining workers
238
+ expect(test4.adjust_workers).to eq({ 'test_worker' => 10 })
239
+ # Rebalance workers across hosts
240
+ expect(test3.adjust_workers).to eq({ 'test_worker' => -5 })
241
+ expect(test2.adjust_workers).to eq({ 'test_worker' => -5 })
242
+ expect(test1.adjust_workers).to eq({ 'test_worker' => -5 })
243
+ expect(test4.adjust_workers).to eq({ 'test_worker' => 15 })
244
+ # No more workers to pick up on the hosts
245
+ expect(test4.adjust_workers).to eq({ 'test_worker' => 0 })
246
+ expect(test3.adjust_workers).to eq({ 'test_worker' => 0 })
247
+ expect(test2.adjust_workers).to eq({ 'test_worker' => 0 })
248
+ expect(test1.adjust_workers).to eq({ 'test_worker' => 0 })
249
+ expect(test4.adjust_workers).to eq({ 'test_worker' => 0 })
250
+ # Release workers
251
+ test4.release_workers
252
+ # Rebalance for lost host
253
+ expect(test1.adjust_workers).to eq({ 'test_worker' => 5 })
254
+ expect(test2.adjust_workers).to eq({ 'test_worker' => 5 })
255
+ expect(test3.adjust_workers).to eq({ 'test_worker' => 5 })
256
+ # No more workers due to max per host limit
257
+ expect(test1.adjust_workers).to eq({ 'test_worker' => 0 })
258
+ expect(test2.adjust_workers).to eq({ 'test_worker' => 0 })
259
+ expect(test3.adjust_workers).to eq({ 'test_worker' => 0 })
260
+ end
261
+
262
+ end
263
+
264
+ context "max workers per host with high maximum worker count" do
265
+ let(:settings) {
266
+ {
267
+ rebalance_flag: true,
268
+ manage_worker_heartbeats: true,
269
+ max_workers_per_host: 5,
270
+ cluster_maximums: {
271
+ 'test_worker1' => '3',
272
+ 'test_worker2' => '3',
273
+ 'test_worker3' => '3',
274
+ 'test_worker4' => '3',
275
+ 'test_worker5' => '3',
276
+ 'test_worker6' => '3'
277
+ }
278
+ }
279
+ }
280
+
281
+ let(:test_client) {
282
+ Redis.new
283
+ }
284
+
285
+ let(:adapter1) {
286
+ adapter1 = Gru::Adapters::RedisAdapter.new(Gru::Configuration.new(settings.clone))
287
+ allow(adapter1).to receive(:hostname).and_return('test1')
288
+ adapter1
289
+ }
290
+
291
+ let(:adapter2) {
292
+ adapter2 = Gru::Adapters::RedisAdapter.new(Gru::Configuration.new(settings.clone))
293
+ allow(adapter2).to receive(:hostname).and_return('test2')
294
+ adapter2
295
+ }
296
+
297
+ let(:adapter3) {
298
+ adapter3 = Gru::Adapters::RedisAdapter.new(Gru::Configuration.new(settings.clone))
299
+ allow(adapter3).to receive(:hostname).and_return('test3')
300
+ adapter3
301
+ }
302
+
303
+ let(:adapter4) {
304
+ adapter4 = Gru::Adapters::RedisAdapter.new(Gru::Configuration.new(settings.clone))
305
+ allow(adapter4).to receive(:hostname).and_return('test4')
306
+ adapter4
307
+ }
308
+
309
+ it "handles provisioning with many workers and few worker instances" do
310
+ test1 = Gru::WorkerManager.new(adapter1)
311
+ test2 = Gru::WorkerManager.new(adapter2)
312
+ test3 = Gru::WorkerManager.new(adapter3)
313
+ test4 = Gru::WorkerManager.new(adapter4)
314
+ test1.register_workers
315
+ test2.register_workers
316
+ test3.register_workers
317
+ # Honors max processes per host setting
318
+ expect(test1.adjust_workers).to eq({
319
+ 'test_worker1' => 1,
320
+ 'test_worker2' => 1,
321
+ 'test_worker3' => 1,
322
+ 'test_worker4' => 1,
323
+ 'test_worker5' => 1,
324
+ 'test_worker6' => 0
325
+ })
326
+
327
+ expect(test2.adjust_workers).to eq({
328
+ 'test_worker1' => 1,
329
+ 'test_worker2' => 1,
330
+ 'test_worker3' => 1,
331
+ 'test_worker4' => 1,
332
+ 'test_worker5' => 1,
333
+ 'test_worker6' => 0
334
+ })
335
+
336
+ expect(test3.adjust_workers).to eq({
337
+ 'test_worker1' => 1,
338
+ 'test_worker2' => 1,
339
+ 'test_worker3' => 1,
340
+ 'test_worker4' => 1,
341
+ 'test_worker5' => 1,
342
+ 'test_worker6' => 0
343
+ })
344
+ test4.register_workers
345
+ # Adds new worker based on new instance
346
+ expect(test4.adjust_workers).to eq({
347
+ 'test_worker1' => 0,
348
+ 'test_worker2' => 0,
349
+ 'test_worker3' => 0,
350
+ 'test_worker4' => 0,
351
+ 'test_worker5' => 0,
352
+ 'test_worker6' => 1
353
+ })
354
+
355
+ # Doesn't alter existing worker instances
356
+ # due to max_workers_per_host balance
357
+ expect(test1.adjust_workers).to eq({
358
+ 'test_worker1' => 0,
359
+ 'test_worker2' => 0,
360
+ 'test_worker3' => 0,
361
+ 'test_worker4' => 0,
362
+ 'test_worker5' => 0,
363
+ 'test_worker6' => 0
364
+ })
365
+ end
366
+
367
+ it "handles expiring with many workers and few worker instances" do
368
+ test1 = Gru::WorkerManager.new(adapter1)
369
+ test1.register_workers
370
+ args = ["GRU:default:default:test1:workers_running",
371
+ 'test_worker1',1,'test_worker2',1,'test_worker3',1,
372
+ 'test_worker4',1,'test_worker5',1,'test_worker6',1
373
+ ]
374
+ client.hmset(*args)
375
+
376
+ # Honors max processes per host setting
377
+ expect(test1.adjust_workers).to eq({
378
+ 'test_worker1' => -1,
379
+ 'test_worker2' => 0,
380
+ 'test_worker3' => 0,
381
+ 'test_worker4' => 0,
382
+ 'test_worker5' => 0,
383
+ 'test_worker6' => 0
384
+ })
385
+
386
+ expect(test1.adjust_workers).to eq({
387
+ 'test_worker1' => 0,
388
+ 'test_worker2' => 0,
389
+ 'test_worker3' => 0,
390
+ 'test_worker4' => 0,
391
+ 'test_worker5' => 0,
392
+ 'test_worker6' => 0
393
+ })
394
+ end
395
+ end
396
+ context "max workers per host without rebalance flag" do
397
+ let(:settings) {
398
+ {
399
+ rebalance_flag: false,
400
+ manage_worker_heartbeats: true,
401
+ max_workers_per_host: 5,
402
+ cluster_maximums: {
403
+ 'test_worker1' => '3',
404
+ 'test_worker2' => '3',
405
+ 'test_worker3' => '3',
406
+ 'test_worker4' => '3',
407
+ 'test_worker5' => '3',
408
+ 'test_worker6' => '3'
409
+ }
410
+ }
411
+ }
412
+
413
+ let(:test_client) {
414
+ Redis.new
415
+ }
416
+
417
+ let(:adapter1) {
418
+ adapter1 = Gru::Adapters::RedisAdapter.new(Gru::Configuration.new(settings.clone))
419
+ allow(adapter1).to receive(:hostname).and_return('test1')
420
+ adapter1
421
+ }
422
+
423
+ let(:adapter2) {
424
+ adapter2 = Gru::Adapters::RedisAdapter.new(Gru::Configuration.new(settings.clone))
425
+ allow(adapter2).to receive(:hostname).and_return('test2')
426
+ adapter2
427
+ }
428
+
429
+ let(:adapter3) {
430
+ adapter3 = Gru::Adapters::RedisAdapter.new(Gru::Configuration.new(settings.clone))
431
+ allow(adapter3).to receive(:hostname).and_return('test3')
432
+ adapter3
433
+ }
434
+
435
+ let(:adapter4) {
436
+ adapter4 = Gru::Adapters::RedisAdapter.new(Gru::Configuration.new(settings.clone))
437
+ allow(adapter4).to receive(:hostname).and_return('test4')
438
+ adapter4
439
+ }
440
+
441
+ it "honors the max workers per host setting" do
442
+ test1 = Gru::WorkerManager.new(adapter1)
443
+ test2 = Gru::WorkerManager.new(adapter2)
444
+ test3 = Gru::WorkerManager.new(adapter3)
445
+ test4 = Gru::WorkerManager.new(adapter4)
446
+ test1.register_workers
447
+ test2.register_workers
448
+ test3.register_workers
449
+ # Honors max processes per host setting
450
+ expect(test1.adjust_workers).to eq({
451
+ 'test_worker1' => 3,
452
+ 'test_worker2' => 2,
453
+ 'test_worker3' => 0,
454
+ 'test_worker4' => 0,
455
+ 'test_worker5' => 0,
456
+ 'test_worker6' => 0
457
+ })
458
+
459
+ expect(test2.adjust_workers).to eq({
460
+ 'test_worker1' => 0,
461
+ 'test_worker2' => 1,
462
+ 'test_worker3' => 3,
463
+ 'test_worker4' => 1,
464
+ 'test_worker5' => 0,
465
+ 'test_worker6' => 0
466
+ })
467
+
468
+ expect(test3.adjust_workers).to eq({
469
+ 'test_worker1' => 0,
470
+ 'test_worker2' => 0,
471
+ 'test_worker3' => 0,
472
+ 'test_worker4' => 2,
473
+ 'test_worker5' => 3,
474
+ 'test_worker6' => 0
475
+ })
476
+
477
+ test4.register_workers
478
+ expect(test4.adjust_workers).to eq({
479
+ 'test_worker1' => 0,
480
+ 'test_worker2' => 0,
481
+ 'test_worker3' => 0,
482
+ 'test_worker4' => 0,
483
+ 'test_worker5' => 0,
484
+ 'test_worker6' => 3
485
+ })
486
+ end
170
487
  end
171
488
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gru
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeffrey Gillis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-21 00:00:00.000000000 Z
11
+ date: 2016-04-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis