asynchronic 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a1dfc813a24c2580723d255461a9a6b91376d562
4
- data.tar.gz: 793f9fd3f28c7fc712f98adeba227128b37644f9
3
+ metadata.gz: b2bd845501396b08b9d2948649a8792af6a966cb
4
+ data.tar.gz: 577a10429b1df9448d84394c85d5f38752f4380b
5
5
  SHA512:
6
- metadata.gz: 1a003d3fefe9a1a4f79535a6dc0e1639306f347672d75b1aef6e625c59da4aaeca68a98d0d8d73f02646d62bcef6ec431836531d7292bdb25c0d1bd4854a4ff9
7
- data.tar.gz: c2d6023bd1ea95d9b6147193bea9be6b36062c5bf774c08cd39e85029e4b5825067902553c14db0be6d05416e82b03ba4bea404376a62c64aad00fd0c0508caa
6
+ metadata.gz: 58b29093b0856f719a24efc276ab827bc83a492dfa3b781ed3ced23ab6d58e6f869989abd08c1bceb35d94712f060dec419f2d643ba1b83917b8eb0ce538feae
7
+ data.tar.gz: 7f7248fdc544ac0bd902311ab4893bc02f0545b689206f1061fab5f6e4e0d334dd36c842024fb7bf9c35c764e3d7527c122ddbf7fd9830f05fce3204676e561e
data/Rakefile CHANGED
@@ -3,7 +3,8 @@ require 'rake/testtask'
3
3
 
4
4
  Rake::TestTask.new(:spec) do |t|
5
5
  t.libs << 'spec'
6
- t.pattern = 'spec/**/*_spec.rb'
6
+ t.libs << 'lib'
7
+ t.pattern = ENV['DIR'] ? File.join(ENV['DIR'], '**', '*_spec.rb') : 'spec/**/*_spec.rb'
7
8
  t.verbose = false
8
9
  t.warning = false
9
10
  t.loader = nil if ENV['TEST']
data/asynchronic.gemspec CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.add_dependency 'ost', '~> 0.1'
23
23
  spec.add_dependency 'class_config', '~> 0.0'
24
24
  spec.add_dependency 'transparent_proxy', '~> 0.0'
25
+ spec.add_dependency 'multi_require', '~> 1.0'
25
26
 
26
27
  spec.add_development_dependency 'bundler', '~> 1.12'
27
28
  spec.add_development_dependency 'rake', '~> 11.0'
data/lib/asynchronic.rb CHANGED
@@ -5,8 +5,9 @@ require 'ost'
5
5
  require 'class_config'
6
6
  require 'transparent_proxy'
7
7
  require 'logger'
8
+ require 'multi_require'
8
9
 
9
- Dir.glob(File.expand_path('asynchronic/**/*.rb', File.dirname(__FILE__))).sort.each { |f| require f }
10
+ MultiRequire.require_relative_pattern 'asynchronic/**/*.rb'
10
11
 
11
12
  module Asynchronic
12
13
 
@@ -16,9 +17,9 @@ module Asynchronic
16
17
  attr_config :queue_engine, QueueEngine::InMemory.new
17
18
  attr_config :data_store, DataStore::InMemory.new
18
19
  attr_config :logger, Logger.new($stdout)
20
+ attr_config :retry_timeout, 30
21
+ attr_config :garbage_collector_timeout, 30
19
22
 
20
- RETRY_TIMEOUT = 30
21
-
22
23
  def self.environment
23
24
  Environment.new queue_engine, data_store
24
25
  end
@@ -31,12 +32,16 @@ module Asynchronic
31
32
  environment.processes
32
33
  end
33
34
 
34
- def self.retry_execution(a_class, message)
35
+ def self.garbage_collector
36
+ @garbage_collector ||= GarbageCollector.new environment, garbage_collector_timeout
37
+ end
38
+
39
+ def self.retry_execution(klass, message)
35
40
  begin
36
41
  result = yield
37
42
  rescue Exception => ex
38
- logger.error(a_class) { "Retrying #{message}. ERROR: #{ex.message}" }
39
- sleep RETRY_TIMEOUT
43
+ logger.error(klass) { "Retrying #{message}. ERROR: #{ex.message}" }
44
+ sleep retry_timeout
40
45
  retry
41
46
  end
42
47
  result
@@ -23,12 +23,17 @@ module Asynchronic
23
23
  @hash.delete key.to_s
24
24
  end
25
25
 
26
+ def delete_cascade(key)
27
+ keys = self.keys.select { |k| k.sections.first == key }
28
+ keys.each { |k| delete k }
29
+ end
30
+
26
31
  def keys
27
- @hash.keys.map { |k| Key.new k }
32
+ @hash.keys.map { |k| Key[k] }
28
33
  end
29
34
 
30
35
  def synchronize(key, &block)
31
- @keys_mutex[key].synchronize &block
36
+ @keys_mutex[key].synchronize(&block)
32
37
  end
33
38
 
34
39
  def connection_args
@@ -3,6 +3,10 @@ module Asynchronic
3
3
  class Key < String
4
4
 
5
5
  SEPARATOR = '|'
6
+
7
+ def self.[](key)
8
+ new key
9
+ end
6
10
 
7
11
  def initialize(key)
8
12
  super key.to_s
@@ -8,8 +8,8 @@ module Asynchronic
8
8
  include Helper
9
9
 
10
10
  def initialize(scope, *args)
11
- @scope = Key.new scope
12
- @connection = ::Redis.new *args
11
+ @scope = Key[scope]
12
+ @connection = ::Redis.new(*args)
13
13
  end
14
14
 
15
15
  def [](key)
@@ -28,8 +28,13 @@ module Asynchronic
28
28
  @connection.del @scope[key]
29
29
  end
30
30
 
31
+ def delete_cascade(key)
32
+ @connection.del @scope[key]
33
+ @connection.keys(@scope[key]['*']).each { |k| @connection.del k }
34
+ end
35
+
31
36
  def keys
32
- @connection.keys(@scope['*']).map { |k| Key.new(k).remove_first }
37
+ @connection.keys(@scope['*']).map { |k| Key[k].remove_first }
33
38
  end
34
39
 
35
40
  def synchronize(key)
@@ -46,7 +51,7 @@ module Asynchronic
46
51
  end
47
52
 
48
53
  def self.connect(*args)
49
- new *args
54
+ new(*args)
50
55
  end
51
56
 
52
57
  end
@@ -9,7 +9,7 @@ module Asynchronic
9
9
 
10
10
  def initialize(data_store, scope)
11
11
  @data_store = data_store
12
- @scope = Key.new scope
12
+ @scope = Key[scope]
13
13
  end
14
14
 
15
15
  def [](key)
@@ -24,10 +24,14 @@ module Asynchronic
24
24
  @data_store.delete @scope[key]
25
25
  end
26
26
 
27
+ def delete_cascade
28
+ @data_store.delete_cascade @scope
29
+ end
30
+
27
31
  def keys
28
32
  @data_store.keys.
29
33
  select { |k| k.start_with? @scope[''] }.
30
- map { |k| Key.new(k).remove_first @scope.sections.count }
34
+ map { |k| Key[k].remove_first @scope.sections.count }
31
35
  end
32
36
 
33
37
  def synchronize(key, &block)
@@ -0,0 +1,61 @@
1
+ module Asynchronic
2
+ class GarbageCollector
3
+
4
+ def initialize(environment, timeout)
5
+ @environment = environment
6
+ @timeout = timeout
7
+ @running = false
8
+ @conditions = {}
9
+ end
10
+
11
+ def start
12
+ Asynchronic.logger.info('Asynchronic') { 'Starting GC' }
13
+
14
+ Signal.trap('QUIT') { stop }
15
+
16
+ @running = true
17
+
18
+ while @running
19
+ processes = environment.processes
20
+
21
+ conditions.each do |name, condition|
22
+ Asynchronic.logger.info('Asynchronic') { "Running GC - #{name}" }
23
+ begin
24
+ processes.select(&condition).each(&:destroy)
25
+ rescue => ex
26
+ Asynchronic.logger.error('Asynchronic') { "#{ex.class}: #{ex.message}" }
27
+ end
28
+ end
29
+
30
+ wait
31
+ end
32
+ end
33
+
34
+ def stop
35
+ Asynchronic.logger.info('Asynchronic') { 'Stopping GC' }
36
+ @running = false
37
+ end
38
+
39
+ def add_condition(name, &block)
40
+ conditions[name] = block
41
+ end
42
+
43
+ def remove_condition(name)
44
+ conditions.delete name
45
+ end
46
+
47
+ def conditions_names
48
+ conditions.keys
49
+ end
50
+
51
+ private
52
+
53
+ attr_reader :environment, :timeout, :conditions
54
+
55
+ def wait
56
+ Asynchronic.logger.info('Asynchronic') { 'Sleeping GC' }
57
+ sleep timeout
58
+ end
59
+
60
+ end
61
+ end
@@ -13,12 +13,14 @@ module Asynchronic
13
13
 
14
14
  ATTRIBUTE_NAMES = [:type, :name, :queue, :status, :dependencies, :data, :result, :error] | TIME_TRACKING_MAP.values.uniq
15
15
 
16
+ CANCELED_ERROR_MESSAGE = 'Canceled'
17
+
16
18
  attr_reader :id
17
19
 
18
20
  def initialize(environment, id, &block)
19
21
  @environment = environment
20
- @id = DataStore::Key.new id
21
- instance_eval &block if block_given?
22
+ @id = DataStore::Key[id]
23
+ instance_eval(&block) if block_given?
22
24
  end
23
25
 
24
26
  ATTRIBUTE_NAMES.each do |attribute|
@@ -41,6 +43,14 @@ module Asynchronic
41
43
  completed? || aborted?
42
44
  end
43
45
 
46
+ def cancel!
47
+ abort! CANCELED_ERROR_MESSAGE
48
+ end
49
+
50
+ def destroy
51
+ data_store.delete_cascade
52
+ end
53
+
44
54
  def full_status
45
55
  processes.each_with_object(name => status) do |process, hash|
46
56
  hash.update(process.full_status)
@@ -60,7 +70,7 @@ module Asynchronic
60
70
  end
61
71
 
62
72
  def [](process_name)
63
- processes.detect { |p| p.name == process_name }
73
+ processes.detect { |p| p.name == process_name.to_s }
64
74
  end
65
75
 
66
76
  def processes
@@ -131,7 +141,7 @@ module Asynchronic
131
141
 
132
142
  new(environment, id) do
133
143
  self.type = type
134
- self.name = params.delete(:alias) || type
144
+ self.name = (params.delete(:alias) || type).to_s
135
145
  self.queue = params.delete(:queue) || type.queue || parent_queue
136
146
  self.dependencies = Array(params.delete(:dependencies)) | Array(params.delete(:dependency)) | infer_dependencies(params)
137
147
  self.params = params
@@ -1,3 +1,3 @@
1
1
  module Asynchronic
2
- VERSION = '1.4.0'
2
+ VERSION = '1.5.0'
3
3
  end
@@ -21,6 +21,23 @@ module DataStoreExamples
21
21
  data_store[:key].must_be_nil
22
22
  end
23
23
 
24
+ it 'Delete cascade' do
25
+ data_store[Key[:key_1]] = 1
26
+ data_store[Key[:key_1][:key_1_1]] = 2
27
+ data_store[Key[:key_1][:key_1_2]] = 3
28
+ data_store[Key[:key_2]] = 4
29
+ data_store[Key[:key_2][:key_2_1]] = 5
30
+ data_store[Key[:key_2][:key_2_2]] = 6
31
+
32
+ data_store.delete_cascade Key[:key_1]
33
+
34
+ data_store.keys.sort.must_equal [
35
+ Key[:key_2],
36
+ Key[:key_2][:key_2_1],
37
+ Key[:key_2][:key_2_2]
38
+ ]
39
+ end
40
+
24
41
  it 'Each' do
25
42
  data_store[:a] = 1
26
43
  data_store[:b] = 2
@@ -5,54 +5,54 @@ describe Asynchronic::DataStore::Key do
5
5
  Key = Asynchronic::DataStore::Key
6
6
 
7
7
  it 'Return the namespace' do
8
- key = Key.new 'foo'
8
+ key = Key['foo']
9
9
  key.must_equal 'foo'
10
10
  end
11
11
 
12
12
  it 'Prepend the namespace' do
13
- key = Key.new 'foo'
13
+ key = Key['foo']
14
14
  key['bar'].must_equal 'foo|bar'
15
15
  end
16
16
 
17
17
  it 'Work in more than one level' do
18
- key_1 = Key.new 'foo'
19
- key_2 = Key.new key_1['bar']
18
+ key_1 = Key['foo']
19
+ key_2 = Key[key_1['bar']]
20
20
  key_2['baz'].must_equal 'foo|bar|baz'
21
21
  end
22
22
 
23
23
  it 'Be chainable' do
24
- key = Key.new 'foo'
24
+ key = Key['foo']
25
25
  key['bar']['baz'].must_equal 'foo|bar|baz'
26
26
  end
27
27
 
28
28
  it 'Accept symbols' do
29
- key = Key.new :foo
29
+ key = Key[:foo]
30
30
  key[:bar].must_equal 'foo|bar'
31
31
  end
32
32
 
33
33
  it 'Accept numbers' do
34
- key = Key.new 'foo'
34
+ key = Key['foo']
35
35
  key[3].must_equal 'foo|3'
36
36
  end
37
37
 
38
38
  it 'Split in sections' do
39
- key = Key.new(:foo)[:bar][:buz]
39
+ key = Key[:foo][:bar][:buz]
40
40
  key.sections.must_equal %w(foo bar buz)
41
41
  end
42
42
 
43
43
  it 'Detect nested sections' do
44
- Key.new(:foo).wont_be :nested?
45
- Key.new(:foo)[:bar].must_be :nested?
44
+ Key[:foo].wont_be :nested?
45
+ Key[:foo][:bar].must_be :nested?
46
46
  end
47
47
 
48
48
  it 'Remove first sections' do
49
- key = Key.new(:foo)[:bar][:buz]
49
+ key = Key[:foo][:bar][:buz]
50
50
  key.remove_first.must_equal 'bar|buz'
51
51
  key.remove_first(2).must_equal 'buz'
52
52
  end
53
53
 
54
54
  it 'Remove last sections' do
55
- key = Key.new(:foo)[:bar][:buz]
55
+ key = Key[:foo][:bar][:buz]
56
56
  key.remove_last.must_equal 'foo|bar'
57
57
  key.remove_last(2).must_equal 'foo'
58
58
  end
data/spec/facade_spec.rb CHANGED
@@ -60,4 +60,8 @@ describe Asynchronic, 'Facade' do
60
60
  end
61
61
  end
62
62
 
63
+ it 'Garbage collector' do
64
+ Asynchronic.garbage_collector.must_be_instance_of Asynchronic::GarbageCollector
65
+ end
66
+
63
67
  end
@@ -493,73 +493,165 @@ module LifeCycleExamples
493
493
 
494
494
  execute queue
495
495
 
496
- process.full_status.must_equal AbortQueuedAfertErrorJob => :waiting,
497
- AbortQueuedAfertErrorJob::Child_1 => :queued,
498
- AbortQueuedAfertErrorJob::Child_2 => :queued,
499
- AbortQueuedAfertErrorJob::Child_3 => :queued,
500
- AbortQueuedAfertErrorJob::Child_4 => :queued
496
+ process.full_status.must_equal 'AbortQueuedAfertErrorJob' => :waiting,
497
+ 'AbortQueuedAfertErrorJob::Child_1' => :queued,
498
+ 'AbortQueuedAfertErrorJob::Child_2' => :queued,
499
+ 'AbortQueuedAfertErrorJob::Child_3' => :queued,
500
+ 'AbortQueuedAfertErrorJob::Child_4' => :queued
501
501
 
502
502
  execute queue
503
503
 
504
- process.full_status.must_equal AbortQueuedAfertErrorJob => :waiting,
505
- AbortQueuedAfertErrorJob::Child_1 => :waiting,
506
- 'Child_1_1' => :queued,
507
- 'Child_1_2' => :queued,
508
- AbortQueuedAfertErrorJob::Child_2 => :queued,
509
- AbortQueuedAfertErrorJob::Child_3 => :queued,
510
- AbortQueuedAfertErrorJob::Child_4 => :queued
504
+ process.full_status.must_equal 'AbortQueuedAfertErrorJob' => :waiting,
505
+ 'AbortQueuedAfertErrorJob::Child_1' => :waiting,
506
+ 'Child_1_1' => :queued,
507
+ 'Child_1_2' => :queued,
508
+ 'AbortQueuedAfertErrorJob::Child_2' => :queued,
509
+ 'AbortQueuedAfertErrorJob::Child_3' => :queued,
510
+ 'AbortQueuedAfertErrorJob::Child_4' => :queued
511
511
 
512
512
  execute queue
513
513
 
514
- process.full_status.must_equal AbortQueuedAfertErrorJob => :waiting,
515
- AbortQueuedAfertErrorJob::Child_1 => :waiting,
516
- 'Child_1_1' => :queued,
517
- 'Child_1_2' => :queued,
518
- AbortQueuedAfertErrorJob::Child_2 => :completed,
519
- AbortQueuedAfertErrorJob::Child_3 => :queued,
520
- AbortQueuedAfertErrorJob::Child_4 => :queued
514
+ process.full_status.must_equal 'AbortQueuedAfertErrorJob' => :waiting,
515
+ 'AbortQueuedAfertErrorJob::Child_1' => :waiting,
516
+ 'Child_1_1' => :queued,
517
+ 'Child_1_2' => :queued,
518
+ 'AbortQueuedAfertErrorJob::Child_2' => :completed,
519
+ 'AbortQueuedAfertErrorJob::Child_3' => :queued,
520
+ 'AbortQueuedAfertErrorJob::Child_4' => :queued
521
521
 
522
522
  execute queue
523
523
 
524
- process.full_status.must_equal AbortQueuedAfertErrorJob => :aborted,
525
- AbortQueuedAfertErrorJob::Child_1 => :waiting,
526
- 'Child_1_1' => :queued,
527
- 'Child_1_2' => :queued,
528
- AbortQueuedAfertErrorJob::Child_2 => :completed,
529
- AbortQueuedAfertErrorJob::Child_3 => :aborted,
530
- AbortQueuedAfertErrorJob::Child_4 => :queued
524
+ process.full_status.must_equal 'AbortQueuedAfertErrorJob' => :aborted,
525
+ 'AbortQueuedAfertErrorJob::Child_1' => :waiting,
526
+ 'Child_1_1' => :queued,
527
+ 'Child_1_2' => :queued,
528
+ 'AbortQueuedAfertErrorJob::Child_2' => :completed,
529
+ 'AbortQueuedAfertErrorJob::Child_3' => :aborted,
530
+ 'AbortQueuedAfertErrorJob::Child_4' => :queued
531
531
 
532
532
  execute queue
533
533
 
534
- process.full_status.must_equal AbortQueuedAfertErrorJob => :aborted,
535
- AbortQueuedAfertErrorJob::Child_1 => :waiting,
536
- 'Child_1_1' => :queued,
537
- 'Child_1_2' => :queued,
538
- AbortQueuedAfertErrorJob::Child_2 => :completed,
539
- AbortQueuedAfertErrorJob::Child_3 => :aborted,
540
- AbortQueuedAfertErrorJob::Child_4 => :aborted
534
+ process.full_status.must_equal 'AbortQueuedAfertErrorJob' => :aborted,
535
+ 'AbortQueuedAfertErrorJob::Child_1' => :waiting,
536
+ 'Child_1_1' => :queued,
537
+ 'Child_1_2' => :queued,
538
+ 'AbortQueuedAfertErrorJob::Child_2' => :completed,
539
+ 'AbortQueuedAfertErrorJob::Child_3' => :aborted,
540
+ 'AbortQueuedAfertErrorJob::Child_4' => :aborted
541
541
 
542
542
  execute queue
543
543
 
544
- process.full_status.must_equal AbortQueuedAfertErrorJob => :aborted,
545
- AbortQueuedAfertErrorJob::Child_1 => :aborted,
546
- 'Child_1_1' => :aborted,
547
- 'Child_1_2' => :queued,
548
- AbortQueuedAfertErrorJob::Child_2 => :completed,
549
- AbortQueuedAfertErrorJob::Child_3 => :aborted,
550
- AbortQueuedAfertErrorJob::Child_4 => :aborted
544
+ process.full_status.must_equal 'AbortQueuedAfertErrorJob' => :aborted,
545
+ 'AbortQueuedAfertErrorJob::Child_1' => :aborted,
546
+ 'Child_1_1' => :aborted,
547
+ 'Child_1_2' => :queued,
548
+ 'AbortQueuedAfertErrorJob::Child_2' => :completed,
549
+ 'AbortQueuedAfertErrorJob::Child_3' => :aborted,
550
+ 'AbortQueuedAfertErrorJob::Child_4' => :aborted
551
551
 
552
552
  execute queue
553
553
 
554
- process.full_status.must_equal AbortQueuedAfertErrorJob => :aborted,
555
- AbortQueuedAfertErrorJob::Child_1 => :aborted,
556
- 'Child_1_1' => :aborted,
557
- 'Child_1_2' => :aborted,
558
- AbortQueuedAfertErrorJob::Child_2 => :completed,
559
- AbortQueuedAfertErrorJob::Child_3 => :aborted,
560
- AbortQueuedAfertErrorJob::Child_4 => :aborted
554
+ process.full_status.must_equal 'AbortQueuedAfertErrorJob' => :aborted,
555
+ 'AbortQueuedAfertErrorJob::Child_1' => :aborted,
556
+ 'Child_1_1' => :aborted,
557
+ 'Child_1_2' => :aborted,
558
+ 'AbortQueuedAfertErrorJob::Child_2' => :completed,
559
+ 'AbortQueuedAfertErrorJob::Child_3' => :aborted,
560
+ 'AbortQueuedAfertErrorJob::Child_4' => :aborted
561
561
 
562
562
  process.real_error.must_equal 'Forced error'
563
563
  end
564
564
 
565
+ it 'Manual abort' do
566
+ process = create NestedJob, input: 10
567
+
568
+ process.enqueue
569
+
570
+ execute queue
571
+
572
+ process.full_status.must_equal 'NestedJob' => :waiting,
573
+ 'NestedJob::Level1' => :queued
574
+
575
+ execute queue
576
+
577
+ process.full_status.must_equal 'NestedJob' => :waiting,
578
+ 'NestedJob::Level1' => :waiting,
579
+ 'NestedJob::Level1::Level2' => :queued
580
+
581
+ process.cancel!
582
+
583
+ process.real_error.must_equal Asynchronic::Process::CANCELED_ERROR_MESSAGE
584
+
585
+ process.full_status.must_equal 'NestedJob' => :aborted,
586
+ 'NestedJob::Level1' => :waiting,
587
+ 'NestedJob::Level1::Level2' => :queued
588
+
589
+ execute queue
590
+
591
+ process.full_status.must_equal 'NestedJob' => :aborted,
592
+ 'NestedJob::Level1' => :aborted,
593
+ 'NestedJob::Level1::Level2' => :aborted
594
+ end
595
+
596
+ it 'Remove process' do
597
+ process_1 = create AliasJob
598
+ process_2 = create AliasJob
599
+
600
+ process_1.enqueue
601
+
602
+ execute queue
603
+
604
+ pid_1 = process_1.id
605
+ pid_2 = process_2.id
606
+
607
+ data_store.keys.select { |k| k.start_with? pid_1 }.count.must_equal 37
608
+ data_store.keys.select { |k| k.start_with? pid_2 }.count.must_equal 7
609
+
610
+ process_1.destroy
611
+
612
+ data_store.keys.select { |k| k.start_with? pid_1 }.count.must_equal 0
613
+ data_store.keys.select { |k| k.start_with? pid_2 }.count.must_equal 7
614
+ end
615
+
616
+ it 'Garbage collector' do
617
+ process_1 = create AliasJob
618
+ process_1.enqueue
619
+ 4.times { execute queue }
620
+
621
+ process_2 = create AliasJob
622
+ process_2.enqueue
623
+ execute queue
624
+
625
+ pid_1 = process_1.id
626
+ pid_2 = process_2.id
627
+
628
+ process_1.must_be_completed
629
+ process_2.must_be_waiting
630
+
631
+ data_store.keys.select { |k| k.start_with? pid_1 }.count.must_equal 49
632
+ data_store.keys.select { |k| k.start_with? pid_2 }.count.must_equal 37
633
+
634
+ gc = Asynchronic::GarbageCollector.new env, 0.001
635
+
636
+ gc.add_condition('Completed', &:completed?)
637
+ gc.add_condition('Waiting', &:waiting?)
638
+ gc.add_condition('Exception') { raise 'Invalid condition' }
639
+
640
+ gc.conditions_names.must_equal ['Completed', 'Waiting', 'Exception']
641
+
642
+ gc.remove_condition 'Waiting'
643
+
644
+ gc.conditions_names.must_equal ['Completed', 'Exception']
645
+
646
+ Thread.new do
647
+ sleep 0.01
648
+ gc.stop
649
+ end
650
+
651
+ gc.start
652
+
653
+ data_store.keys.select { |k| k.start_with? pid_1 }.count.must_equal 0
654
+ data_store.keys.select { |k| k.start_with? pid_2 }.count.must_equal 37
655
+ end
656
+
565
657
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asynchronic
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gabriel Naiman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-20 00:00:00.000000000 Z
11
+ date: 2018-11-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: multi_require
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: bundler
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -221,6 +235,7 @@ files:
221
235
  - lib/asynchronic/data_store/scoped_store.rb
222
236
  - lib/asynchronic/environment.rb
223
237
  - lib/asynchronic/error.rb
238
+ - lib/asynchronic/garbage_collector.rb
224
239
  - lib/asynchronic/job.rb
225
240
  - lib/asynchronic/process.rb
226
241
  - lib/asynchronic/queue_engine/in_memory.rb