asynchronic 3.0.1 → 4.0.1
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 +5 -5
- data/.travis.yml +7 -10
- data/README.md +1 -2
- data/asynchronic.gemspec +2 -3
- data/lib/asynchronic.rb +8 -0
- data/lib/asynchronic/data_store/in_memory.rb +17 -15
- data/lib/asynchronic/data_store/key.rb +3 -3
- data/lib/asynchronic/data_store/lazy_value.rb +5 -3
- data/lib/asynchronic/data_store/redis.rb +22 -14
- data/lib/asynchronic/data_store/scoped_store.rb +18 -19
- data/lib/asynchronic/environment.rb +3 -3
- data/lib/asynchronic/error.rb +2 -3
- data/lib/asynchronic/garbage_collector.rb +2 -1
- data/lib/asynchronic/job.rb +12 -12
- data/lib/asynchronic/notifier/broadcaster.rb +8 -4
- data/lib/asynchronic/process.rb +42 -40
- data/lib/asynchronic/queue_engine/in_memory.rb +17 -11
- data/lib/asynchronic/queue_engine/ost.rb +7 -5
- data/lib/asynchronic/queue_engine/synchronic.rb +19 -7
- data/lib/asynchronic/version.rb +1 -1
- data/lib/asynchronic/worker.rb +7 -10
- data/spec/data_store/data_store_examples.rb +17 -9
- data/spec/data_store/in_memory_spec.rb +0 -2
- data/spec/data_store/key_spec.rb +1 -1
- data/spec/data_store/lazy_value_examples.rb +7 -5
- data/spec/data_store/redis_spec.rb +4 -10
- data/spec/expectations.rb +2 -2
- data/spec/facade_spec.rb +3 -3
- data/spec/jobs.rb +10 -10
- data/spec/minitest_helper.rb +5 -12
- data/spec/process/life_cycle_examples.rb +25 -23
- data/spec/process/life_cycle_in_memory_spec.rb +0 -1
- data/spec/process/life_cycle_redis_spec.rb +0 -1
- data/spec/queue_engine/in_memory_spec.rb +1 -3
- data/spec/queue_engine/ost_spec.rb +1 -7
- data/spec/queue_engine/queue_engine_examples.rb +17 -9
- data/spec/queue_engine/synchronic_spec.rb +1 -1
- data/spec/worker/in_memory_spec.rb +1 -2
- data/spec/worker/redis_spec.rb +0 -6
- data/spec/worker/worker_examples.rb +6 -4
- metadata +11 -20
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 1927d93fad4703789a566429a5e31767e332ca9f17cbe94db92f899587398a38
|
|
4
|
+
data.tar.gz: 64a6f3c8fc8d9248166ba6b1fc0dcc4f9d51a6229f3a2f3d4b99b645dd92059f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '0207949f35f7e91d8b5506d469e85f023071a5bbfbc17631fbbf4f5da76e22f2224d4bc08ecf347fe0b34f06e08d063c3db1e1b7d0c81a6ff164c4d9e1ba21a7'
|
|
7
|
+
data.tar.gz: 17f48dc8a1bb5aed62d28aa5a22d1c6f6271fa5b39a0032fce098148c22185a88803e741760b8aa8b35a3b765d5675136b9808b62e996bd0138d8cac5d866b2a
|
data/.travis.yml
CHANGED
|
@@ -4,12 +4,13 @@ rvm:
|
|
|
4
4
|
- 2.0
|
|
5
5
|
- 2.1
|
|
6
6
|
- 2.2
|
|
7
|
-
- 2.3
|
|
8
|
-
- 2.4
|
|
9
|
-
- 2.5
|
|
10
|
-
- 2.6
|
|
11
|
-
-
|
|
12
|
-
-
|
|
7
|
+
- 2.3
|
|
8
|
+
- 2.4
|
|
9
|
+
- 2.5
|
|
10
|
+
- 2.6
|
|
11
|
+
- 2.7
|
|
12
|
+
- 3.0
|
|
13
|
+
- jruby-9.2.9.0
|
|
13
14
|
- ruby-head
|
|
14
15
|
- jruby-head
|
|
15
16
|
|
|
@@ -19,9 +20,5 @@ matrix:
|
|
|
19
20
|
- rvm: ruby-head
|
|
20
21
|
- rvm: jruby-head
|
|
21
22
|
|
|
22
|
-
before_install:
|
|
23
|
-
- rvm all-gemsets do gem uninstall bundler -ax || true
|
|
24
|
-
- gem install bundler -v "< 2"
|
|
25
|
-
|
|
26
23
|
services:
|
|
27
24
|
- redis-server
|
data/README.md
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
# Asynchronic
|
|
2
2
|
|
|
3
3
|
[](https://rubygems.org/gems/asynchronic)
|
|
4
|
-
[](https://travis-ci.com/gabynaiman/asynchronic)
|
|
5
5
|
[](https://coveralls.io/r/gabynaiman/asynchronic?branch=master)
|
|
6
6
|
[](https://codeclimate.com/github/gabynaiman/asynchronic)
|
|
7
|
-
[](https://gemnasium.com/gabynaiman/asynchronic)
|
|
8
7
|
|
|
9
8
|
DSL for asynchronic pipeline using queues over Redis
|
|
10
9
|
|
data/asynchronic.gemspec
CHANGED
|
@@ -19,13 +19,12 @@ Gem::Specification.new do |spec|
|
|
|
19
19
|
spec.require_paths = ['lib']
|
|
20
20
|
|
|
21
21
|
spec.add_dependency 'ost', '~> 1.0'
|
|
22
|
-
spec.add_dependency 'broadcaster', '~> 1.0'
|
|
22
|
+
spec.add_dependency 'broadcaster', '~> 1.0', '>= 1.0.2'
|
|
23
23
|
spec.add_dependency 'class_config', '~> 0.0'
|
|
24
24
|
spec.add_dependency 'transparent_proxy', '~> 0.0'
|
|
25
25
|
spec.add_dependency 'multi_require', '~> 1.0'
|
|
26
26
|
|
|
27
|
-
spec.add_development_dependency '
|
|
28
|
-
spec.add_development_dependency 'rake', '~> 11.0'
|
|
27
|
+
spec.add_development_dependency 'rake', '~> 12.0'
|
|
29
28
|
spec.add_development_dependency 'minitest', '~> 5.0', '< 5.11'
|
|
30
29
|
spec.add_development_dependency 'minitest-great_expectations', '~> 0.0'
|
|
31
30
|
spec.add_development_dependency 'minitest-stub_any_instance', '~> 1.0'
|
data/lib/asynchronic.rb
CHANGED
|
@@ -23,6 +23,8 @@ module Asynchronic
|
|
|
23
23
|
attr_config :logger, Logger.new($stdout)
|
|
24
24
|
attr_config :retry_timeout, 30
|
|
25
25
|
attr_config :garbage_collector_timeout, 30
|
|
26
|
+
attr_config :redis_client, Redic
|
|
27
|
+
attr_config :redis_settings, 'redis://localhost:6379'
|
|
26
28
|
attr_config :redis_data_store_sync_timeout, 0.01
|
|
27
29
|
attr_config :keep_alive_timeout, 1
|
|
28
30
|
attr_config :connection_name, "HOST=#{Socket.gethostname},PID=#{::Process.pid},UUID=#{SecureRandom.uuid}"
|
|
@@ -43,6 +45,12 @@ module Asynchronic
|
|
|
43
45
|
@garbage_collector ||= GarbageCollector.new environment, garbage_collector_timeout
|
|
44
46
|
end
|
|
45
47
|
|
|
48
|
+
def self.establish_redis_connection(options={})
|
|
49
|
+
redis_client = options.fetch(:redis_client, Asynchronic.redis_client)
|
|
50
|
+
redis_settings = options.fetch(:redis_settings, Asynchronic.redis_settings)
|
|
51
|
+
redis_client.new redis_settings
|
|
52
|
+
end
|
|
53
|
+
|
|
46
54
|
def self.retry_execution(klass, message)
|
|
47
55
|
begin
|
|
48
56
|
result = yield
|
|
@@ -4,7 +4,15 @@ module Asynchronic
|
|
|
4
4
|
|
|
5
5
|
include Helper
|
|
6
6
|
|
|
7
|
-
def
|
|
7
|
+
def self.connect(object_id)
|
|
8
|
+
connections[object_id]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.connections
|
|
12
|
+
@connections ||= {}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
8
16
|
@hash = {}
|
|
9
17
|
@mutex = Mutex.new
|
|
10
18
|
@keys_mutex = Hash.new { |h,k| h[k] = Mutex.new }
|
|
@@ -12,43 +20,37 @@ module Asynchronic
|
|
|
12
20
|
end
|
|
13
21
|
|
|
14
22
|
def [](key)
|
|
15
|
-
Marshal.load(
|
|
23
|
+
Marshal.load(hash[key.to_s]) if hash.key? key.to_s
|
|
16
24
|
end
|
|
17
25
|
|
|
18
26
|
def []=(key, value)
|
|
19
|
-
|
|
27
|
+
mutex.synchronize { hash[key.to_s] = Marshal.dump(value) }
|
|
20
28
|
end
|
|
21
29
|
|
|
22
30
|
def delete(key)
|
|
23
|
-
|
|
31
|
+
hash.delete key.to_s
|
|
24
32
|
end
|
|
25
33
|
|
|
26
34
|
def delete_cascade(key)
|
|
27
|
-
keys
|
|
28
|
-
|
|
35
|
+
keys.select { |k| k.sections.first == key }
|
|
36
|
+
.each { |k| delete k }
|
|
29
37
|
end
|
|
30
38
|
|
|
31
39
|
def keys
|
|
32
|
-
|
|
40
|
+
hash.keys.map { |k| Key[k] }
|
|
33
41
|
end
|
|
34
42
|
|
|
35
43
|
def synchronize(key, &block)
|
|
36
|
-
|
|
44
|
+
keys_mutex[key].synchronize(&block)
|
|
37
45
|
end
|
|
38
46
|
|
|
39
47
|
def connection_args
|
|
40
48
|
[object_id]
|
|
41
49
|
end
|
|
42
50
|
|
|
43
|
-
def self.connect(object_id)
|
|
44
|
-
connections[object_id]
|
|
45
|
-
end
|
|
46
|
-
|
|
47
51
|
private
|
|
48
52
|
|
|
49
|
-
|
|
50
|
-
@connections ||= {}
|
|
51
|
-
end
|
|
53
|
+
attr_reader :hash, :mutex, :keys_mutex
|
|
52
54
|
|
|
53
55
|
end
|
|
54
56
|
end
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
module Asynchronic
|
|
2
2
|
module DataStore
|
|
3
3
|
class Key < String
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
SEPARATOR = '|'
|
|
6
6
|
|
|
7
7
|
def self.[](key)
|
|
8
8
|
new key
|
|
9
9
|
end
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
def initialize(key)
|
|
12
12
|
super key.to_s
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def [](key)
|
|
16
|
-
self.class.new [self,key].join(SEPARATOR)
|
|
16
|
+
self.class.new [self, key].join(SEPARATOR)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def sections
|
|
@@ -14,11 +14,11 @@ module Asynchronic
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def inspect
|
|
17
|
-
"#<#{proxy_class} @data_store_class=#{
|
|
17
|
+
"#<#{proxy_class} @data_store_class=#{data_store_class} @data_store_connection_args=#{data_store_connection_args} @key=#{key}>"
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def data_store
|
|
21
|
-
|
|
21
|
+
data_store_class.connect(*data_store_connection_args)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def to_value
|
|
@@ -27,8 +27,10 @@ module Asynchronic
|
|
|
27
27
|
|
|
28
28
|
private
|
|
29
29
|
|
|
30
|
+
attr_reader :data_store_class, :data_store_connection_args, :key
|
|
31
|
+
|
|
30
32
|
def __getobj__
|
|
31
|
-
@value ||= data_store[
|
|
33
|
+
@value ||= data_store[key]
|
|
32
34
|
end
|
|
33
35
|
|
|
34
36
|
end
|
|
@@ -6,13 +6,17 @@ module Asynchronic
|
|
|
6
6
|
|
|
7
7
|
include Helper
|
|
8
8
|
|
|
9
|
-
def
|
|
9
|
+
def self.connect(*args)
|
|
10
|
+
new(*args)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize(scope, options={})
|
|
10
14
|
@scope = Key[scope]
|
|
11
|
-
@
|
|
15
|
+
@options = options
|
|
12
16
|
end
|
|
13
17
|
|
|
14
18
|
def [](key)
|
|
15
|
-
value =
|
|
19
|
+
value = redis.call! 'GET', scope[key]
|
|
16
20
|
value ? Marshal.load(value) : nil
|
|
17
21
|
rescue => ex
|
|
18
22
|
Asynchronic.logger.warn('Asynchronic') { ex.message }
|
|
@@ -20,39 +24,43 @@ module Asynchronic
|
|
|
20
24
|
end
|
|
21
25
|
|
|
22
26
|
def []=(key, value)
|
|
23
|
-
|
|
27
|
+
redis.call! 'SET', scope[key], Marshal.dump(value)
|
|
24
28
|
end
|
|
25
29
|
|
|
26
30
|
def delete(key)
|
|
27
|
-
|
|
31
|
+
redis.call! 'DEL', scope[key]
|
|
28
32
|
end
|
|
29
33
|
|
|
30
34
|
def delete_cascade(key)
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
redis.call! 'DEL', scope[key]
|
|
36
|
+
redis.call!('KEYS', scope[key]['*']).each { |k| redis.call! 'DEL', k }
|
|
33
37
|
end
|
|
34
38
|
|
|
35
39
|
def keys
|
|
36
|
-
|
|
40
|
+
redis.call!('KEYS', scope['*']).map { |k| Key[k].remove_first }
|
|
37
41
|
end
|
|
38
42
|
|
|
39
43
|
def synchronize(key)
|
|
40
|
-
while
|
|
44
|
+
while redis.call!('GETSET', scope[key][LOCKED], LOCKED) == LOCKED
|
|
41
45
|
sleep Asynchronic.redis_data_store_sync_timeout
|
|
42
46
|
end
|
|
43
47
|
yield
|
|
44
48
|
ensure
|
|
45
|
-
|
|
49
|
+
redis.call! 'DEL', scope[key][LOCKED]
|
|
46
50
|
end
|
|
47
51
|
|
|
48
52
|
def connection_args
|
|
49
|
-
[
|
|
53
|
+
[scope, options]
|
|
50
54
|
end
|
|
51
55
|
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
attr_reader :scope, :options
|
|
59
|
+
|
|
60
|
+
def redis
|
|
61
|
+
@redis ||= Asynchronic.establish_redis_connection options
|
|
54
62
|
end
|
|
55
|
-
|
|
63
|
+
|
|
56
64
|
end
|
|
57
65
|
end
|
|
58
66
|
end
|
|
@@ -4,34 +4,38 @@ module Asynchronic
|
|
|
4
4
|
|
|
5
5
|
include Helper
|
|
6
6
|
|
|
7
|
-
attr_reader :data_store
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
attr_reader :data_store, :scope
|
|
8
|
+
|
|
9
|
+
def self.connect(options)
|
|
10
|
+
data_store = options[:data_store_class].connect(*options[:data_store_connection_args])
|
|
11
|
+
new data_store, options[:scope]
|
|
12
|
+
end
|
|
13
|
+
|
|
10
14
|
def initialize(data_store, scope)
|
|
11
15
|
@data_store = data_store
|
|
12
16
|
@scope = Key[scope]
|
|
13
17
|
end
|
|
14
18
|
|
|
15
19
|
def [](key)
|
|
16
|
-
|
|
20
|
+
data_store[scope[key]]
|
|
17
21
|
end
|
|
18
22
|
|
|
19
23
|
def []=(key, value)
|
|
20
|
-
|
|
24
|
+
data_store[scope[key]] = value
|
|
21
25
|
end
|
|
22
26
|
|
|
23
27
|
def delete(key)
|
|
24
|
-
|
|
28
|
+
data_store.delete scope[key]
|
|
25
29
|
end
|
|
26
30
|
|
|
27
31
|
def delete_cascade
|
|
28
|
-
|
|
32
|
+
data_store.delete_cascade scope
|
|
29
33
|
end
|
|
30
34
|
|
|
31
35
|
def keys
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
data_store.keys
|
|
37
|
+
.select { |k| k.start_with? scope[''] }
|
|
38
|
+
.map { |k| Key[k].remove_first scope.sections.count }
|
|
35
39
|
end
|
|
36
40
|
|
|
37
41
|
def synchronize(key, &block)
|
|
@@ -41,20 +45,15 @@ module Asynchronic
|
|
|
41
45
|
def connection_args
|
|
42
46
|
[
|
|
43
47
|
{
|
|
44
|
-
data_store_class:
|
|
45
|
-
data_store_connection_args:
|
|
46
|
-
scope:
|
|
48
|
+
data_store_class: data_store.class,
|
|
49
|
+
data_store_connection_args: data_store.connection_args,
|
|
50
|
+
scope: scope
|
|
47
51
|
}
|
|
48
52
|
]
|
|
49
53
|
end
|
|
50
54
|
|
|
51
|
-
def self.connect(*args)
|
|
52
|
-
data_store = args[0][:data_store_class].connect *args[0][:data_store_connection_args]
|
|
53
|
-
new data_store, args[0][:scope]
|
|
54
|
-
end
|
|
55
|
-
|
|
56
55
|
def to_s
|
|
57
|
-
"#<#{self.class} @data_store=#{
|
|
56
|
+
"#<#{self.class} @data_store=#{data_store} @scope=#{scope}>"
|
|
58
57
|
end
|
|
59
58
|
|
|
60
59
|
end
|
|
@@ -2,7 +2,7 @@ module Asynchronic
|
|
|
2
2
|
class Environment
|
|
3
3
|
|
|
4
4
|
attr_reader :queue_engine, :data_store, :notifier
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
def initialize(queue_engine, data_store, notifier)
|
|
7
7
|
@queue_engine = queue_engine
|
|
8
8
|
@data_store = data_store
|
|
@@ -14,7 +14,7 @@ module Asynchronic
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def default_queue
|
|
17
|
-
queue
|
|
17
|
+
queue queue_engine.default_queue
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def enqueue(msg, queue=nil)
|
|
@@ -28,7 +28,7 @@ module Asynchronic
|
|
|
28
28
|
def load_process(id)
|
|
29
29
|
Process.new self, id
|
|
30
30
|
end
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
def processes
|
|
33
33
|
Process.all self
|
|
34
34
|
end
|
data/lib/asynchronic/error.rb
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
module Asynchronic
|
|
2
2
|
class Error
|
|
3
3
|
|
|
4
|
-
attr_reader :message
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
attr_reader :message, :backtrace
|
|
5
|
+
|
|
7
6
|
def initialize(source)
|
|
8
7
|
@message = source.respond_to?(:message) ? source.message : source.to_s
|
|
9
8
|
@backtrace = source.respond_to?(:backtrace) ? source.backtrace : []
|
|
@@ -25,7 +25,8 @@ module Asynchronic
|
|
|
25
25
|
begin
|
|
26
26
|
processes.select(&condition).each(&:destroy)
|
|
27
27
|
rescue => ex
|
|
28
|
-
|
|
28
|
+
error_message = "#{ex.class}: #{ex.message}\n#{ex.backtrace.join("\n")}"
|
|
29
|
+
Asynchronic.logger.error('Asynchronic') { error_message }
|
|
29
30
|
end
|
|
30
31
|
end
|
|
31
32
|
|
data/lib/asynchronic/job.rb
CHANGED
|
@@ -1,18 +1,6 @@
|
|
|
1
1
|
module Asynchronic
|
|
2
2
|
class Job
|
|
3
3
|
|
|
4
|
-
def initialize(process)
|
|
5
|
-
@process = process
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
def params
|
|
9
|
-
@process.params
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def result(reference)
|
|
13
|
-
@process[reference].result
|
|
14
|
-
end
|
|
15
|
-
|
|
16
4
|
def self.queue(name=nil)
|
|
17
5
|
name ? @queue = name : @queue
|
|
18
6
|
end
|
|
@@ -23,6 +11,18 @@ module Asynchronic
|
|
|
23
11
|
process.id
|
|
24
12
|
end
|
|
25
13
|
|
|
14
|
+
def initialize(process)
|
|
15
|
+
@process = process
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def params
|
|
19
|
+
process.params
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def result(reference)
|
|
23
|
+
process[reference].result
|
|
24
|
+
end
|
|
25
|
+
|
|
26
26
|
private
|
|
27
27
|
|
|
28
28
|
attr_reader :process
|