asynchronic 1.4.0 → 1.5.0
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 +4 -4
- data/Rakefile +2 -1
- data/asynchronic.gemspec +1 -0
- data/lib/asynchronic.rb +11 -6
- data/lib/asynchronic/data_store/in_memory.rb +7 -2
- data/lib/asynchronic/data_store/key.rb +4 -0
- data/lib/asynchronic/data_store/redis.rb +9 -4
- data/lib/asynchronic/data_store/scoped_store.rb +6 -2
- data/lib/asynchronic/garbage_collector.rb +61 -0
- data/lib/asynchronic/process.rb +14 -4
- data/lib/asynchronic/version.rb +1 -1
- data/spec/data_store/data_store_examples.rb +17 -0
- data/spec/data_store/key_spec.rb +12 -12
- data/spec/facade_spec.rb +4 -0
- data/spec/process/life_cycle_examples.rb +139 -47
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b2bd845501396b08b9d2948649a8792af6a966cb
|
4
|
+
data.tar.gz: 577a10429b1df9448d84394c85d5f38752f4380b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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
|
-
|
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.
|
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(
|
39
|
-
sleep
|
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
|
32
|
+
@hash.keys.map { |k| Key[k] }
|
28
33
|
end
|
29
34
|
|
30
35
|
def synchronize(key, &block)
|
31
|
-
@keys_mutex[key].synchronize
|
36
|
+
@keys_mutex[key].synchronize(&block)
|
32
37
|
end
|
33
38
|
|
34
39
|
def connection_args
|
@@ -8,8 +8,8 @@ module Asynchronic
|
|
8
8
|
include Helper
|
9
9
|
|
10
10
|
def initialize(scope, *args)
|
11
|
-
@scope = Key
|
12
|
-
@connection = ::Redis.new
|
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
|
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
|
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
|
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
|
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
|
data/lib/asynchronic/process.rb
CHANGED
@@ -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
|
21
|
-
instance_eval
|
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
|
data/lib/asynchronic/version.rb
CHANGED
@@ -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
|
data/spec/data_store/key_spec.rb
CHANGED
@@ -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
|
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
|
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
|
19
|
-
key_2 = Key
|
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
|
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
|
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
|
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
|
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
|
45
|
-
Key
|
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
|
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
|
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
@@ -493,73 +493,165 @@ module LifeCycleExamples
|
|
493
493
|
|
494
494
|
execute queue
|
495
495
|
|
496
|
-
process.full_status.must_equal AbortQueuedAfertErrorJob
|
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
|
505
|
-
AbortQueuedAfertErrorJob::Child_1 => :waiting,
|
506
|
-
'Child_1_1'
|
507
|
-
'Child_1_2'
|
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
|
515
|
-
AbortQueuedAfertErrorJob::Child_1 => :waiting,
|
516
|
-
'Child_1_1'
|
517
|
-
'Child_1_2'
|
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
|
525
|
-
AbortQueuedAfertErrorJob::Child_1 => :waiting,
|
526
|
-
'Child_1_1'
|
527
|
-
'Child_1_2'
|
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
|
535
|
-
AbortQueuedAfertErrorJob::Child_1 => :waiting,
|
536
|
-
'Child_1_1'
|
537
|
-
'Child_1_2'
|
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
|
545
|
-
AbortQueuedAfertErrorJob::Child_1 => :aborted,
|
546
|
-
'Child_1_1'
|
547
|
-
'Child_1_2'
|
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
|
555
|
-
AbortQueuedAfertErrorJob::Child_1 => :aborted,
|
556
|
-
'Child_1_1'
|
557
|
-
'Child_1_2'
|
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
|
+
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-
|
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
|