asynchronic 2.0.1 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +7 -10
  3. data/README.md +1 -2
  4. data/asynchronic.gemspec +3 -2
  5. data/lib/asynchronic.rb +13 -3
  6. data/lib/asynchronic/data_store/in_memory.rb +17 -15
  7. data/lib/asynchronic/data_store/key.rb +3 -3
  8. data/lib/asynchronic/data_store/lazy_value.rb +5 -3
  9. data/lib/asynchronic/data_store/redis.rb +22 -14
  10. data/lib/asynchronic/data_store/scoped_store.rb +18 -19
  11. data/lib/asynchronic/environment.rb +6 -6
  12. data/lib/asynchronic/error.rb +2 -3
  13. data/lib/asynchronic/garbage_collector.rb +2 -0
  14. data/lib/asynchronic/job.rb +12 -12
  15. data/lib/asynchronic/notifier/broadcaster.rb +34 -0
  16. data/lib/asynchronic/notifier/in_memory.rb +33 -0
  17. data/lib/asynchronic/process.rb +52 -38
  18. data/lib/asynchronic/queue_engine/in_memory.rb +17 -11
  19. data/lib/asynchronic/queue_engine/ost.rb +15 -12
  20. data/lib/asynchronic/queue_engine/synchronic.rb +19 -7
  21. data/lib/asynchronic/version.rb +1 -1
  22. data/lib/asynchronic/worker.rb +7 -10
  23. data/spec/data_store/data_store_examples.rb +17 -9
  24. data/spec/data_store/in_memory_spec.rb +0 -2
  25. data/spec/data_store/key_spec.rb +1 -1
  26. data/spec/data_store/lazy_value_examples.rb +7 -6
  27. data/spec/data_store/redis_spec.rb +4 -10
  28. data/spec/expectations.rb +2 -2
  29. data/spec/facade_spec.rb +5 -5
  30. data/spec/jobs.rb +12 -12
  31. data/spec/minitest_helper.rb +6 -12
  32. data/spec/process/life_cycle_examples.rb +111 -64
  33. data/spec/process/life_cycle_in_memory_spec.rb +1 -1
  34. data/spec/process/life_cycle_redis_spec.rb +1 -1
  35. data/spec/queue_engine/in_memory_spec.rb +1 -3
  36. data/spec/queue_engine/ost_spec.rb +1 -7
  37. data/spec/queue_engine/queue_engine_examples.rb +17 -9
  38. data/spec/queue_engine/synchronic_spec.rb +1 -1
  39. data/spec/worker/in_memory_spec.rb +2 -2
  40. data/spec/worker/redis_spec.rb +1 -6
  41. data/spec/worker/worker_examples.rb +7 -5
  42. metadata +38 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 1e125dac6480974894d48275d0015bb85410a9f4
4
- data.tar.gz: 07b4801fd659a5c0735ccac5dc24c5340363afa8
2
+ SHA256:
3
+ metadata.gz: 47872aa8b0fdee31f769749a1485e7be5ca2d5f9b7de51863c223547cad4849b
4
+ data.tar.gz: d7f0984211e6bfc46d1960a3b53aef3d3f244a619005504d7ba5e6c27612dbdd
5
5
  SHA512:
6
- metadata.gz: d68006d635184823ac8bcdc4669690b4778b295b4eb98c5b8b4375c0284be30db220ee87bf3f6f7fc932365adb26a3196116b6549ef3432fa2391ec1395f7363
7
- data.tar.gz: 630f880110f71041ab2bc18f5ad5258298b8f3d18560315569aa18ed8459986af20c3768cc3dc77992c82db3d2dce496125ad4931902cddd6ad11d4aaa8fe09e
6
+ metadata.gz: 1893074c7181e0eea27a276e781fdfc1680c909986ccbc80645aeafbd08ef44e3d23cfc82a3480bf1f453987aa75f0667aafd9b5c1f10ddfed5ec28067f2684b
7
+ data.tar.gz: f580a9b1660a3e1aa5fcac083b27cb73c169e2d68ac974114ddff6eb2cc24267deac81af780c4ccc2b84106021e6fbd554269674af9ec89bacd26f5f7c1714d0
data/.travis.yml CHANGED
@@ -4,12 +4,13 @@ rvm:
4
4
  - 2.0
5
5
  - 2.1
6
6
  - 2.2
7
- - 2.3.0
8
- - 2.4.0
9
- - 2.5.0
10
- - 2.6.0
11
- - jruby-9.1.17.0
12
- - jruby-9.2.7.0
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
  [![Gem Version](https://badge.fury.io/rb/asynchronic.svg)](https://rubygems.org/gems/asynchronic)
4
- [![Build Status](https://travis-ci.org/gabynaiman/asynchronic.svg?branch=master)](https://travis-ci.org/gabynaiman/asynchronic)
4
+ [![Build Status](https://travis-ci.com/gabynaiman/asynchronic.svg?branch=master)](https://travis-ci.com/gabynaiman/asynchronic)
5
5
  [![Coverage Status](https://coveralls.io/repos/gabynaiman/asynchronic/badge.svg?branch=master)](https://coveralls.io/r/gabynaiman/asynchronic?branch=master)
6
6
  [![Code Climate](https://codeclimate.com/github/gabynaiman/asynchronic.svg)](https://codeclimate.com/github/gabynaiman/asynchronic)
7
- [![Dependency Status](https://gemnasium.com/gabynaiman/asynchronic.svg)](https://gemnasium.com/gabynaiman/asynchronic)
8
7
 
9
8
  DSL for asynchronic pipeline using queues over Redis
10
9
 
data/asynchronic.gemspec CHANGED
@@ -19,14 +19,15 @@ 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', '>= 1.0.2'
22
23
  spec.add_dependency 'class_config', '~> 0.0'
23
24
  spec.add_dependency 'transparent_proxy', '~> 0.0'
24
25
  spec.add_dependency 'multi_require', '~> 1.0'
25
26
 
26
- spec.add_development_dependency 'bundler', '~> 1.12'
27
- spec.add_development_dependency 'rake', '~> 11.0'
27
+ spec.add_development_dependency 'rake', '~> 12.0'
28
28
  spec.add_development_dependency 'minitest', '~> 5.0', '< 5.11'
29
29
  spec.add_development_dependency 'minitest-great_expectations', '~> 0.0'
30
+ spec.add_development_dependency 'minitest-stub_any_instance', '~> 1.0'
30
31
  spec.add_development_dependency 'minitest-colorin', '~> 0.1'
31
32
  spec.add_development_dependency 'minitest-line', '~> 0.6'
32
33
  spec.add_development_dependency 'simplecov', '~> 0.12'
data/lib/asynchronic.rb CHANGED
@@ -2,6 +2,7 @@ require 'forwardable'
2
2
  require 'securerandom'
3
3
  require 'ost'
4
4
  require 'redic'
5
+ require 'broadcaster'
5
6
  require 'class_config'
6
7
  require 'transparent_proxy'
7
8
  require 'logger'
@@ -18,15 +19,18 @@ module Asynchronic
18
19
  attr_config :default_queue, :asynchronic
19
20
  attr_config :queue_engine, QueueEngine::InMemory.new
20
21
  attr_config :data_store, DataStore::InMemory.new
22
+ attr_config :notifier, Notifier::InMemory.new
21
23
  attr_config :logger, Logger.new($stdout)
22
24
  attr_config :retry_timeout, 30
23
25
  attr_config :garbage_collector_timeout, 30
26
+ attr_config :redis_client, Redic
27
+ attr_config :redis_settings, 'redis://localhost:6379'
24
28
  attr_config :redis_data_store_sync_timeout, 0.01
25
- attr_config :keep_alive_timeout, 0.1
26
- attr_config :connection_name, "HOST=#{Socket.gethostname},PID=#{::Process.pid}"
29
+ attr_config :keep_alive_timeout, 1
30
+ attr_config :connection_name, "HOST=#{Socket.gethostname},PID=#{::Process.pid},UUID=#{SecureRandom.uuid}"
27
31
 
28
32
  def self.environment
29
- Environment.new queue_engine, data_store
33
+ Environment.new queue_engine, data_store, notifier
30
34
  end
31
35
 
32
36
  def self.[](pid)
@@ -41,6 +45,12 @@ module Asynchronic
41
45
  @garbage_collector ||= GarbageCollector.new environment, garbage_collector_timeout
42
46
  end
43
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
+
44
54
  def self.retry_execution(klass, message)
45
55
  begin
46
56
  result = yield
@@ -4,7 +4,15 @@ module Asynchronic
4
4
 
5
5
  include Helper
6
6
 
7
- def initialize(hash={})
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(@hash[key.to_s]) if @hash.key? key.to_s
23
+ Marshal.load(hash[key.to_s]) if hash.key? key.to_s
16
24
  end
17
25
 
18
26
  def []=(key, value)
19
- @mutex.synchronize { @hash[key.to_s] = Marshal.dump(value) }
27
+ mutex.synchronize { hash[key.to_s] = Marshal.dump(value) }
20
28
  end
21
29
 
22
30
  def delete(key)
23
- @hash.delete key.to_s
31
+ hash.delete key.to_s
24
32
  end
25
33
 
26
34
  def delete_cascade(key)
27
- keys = self.keys.select { |k| k.sections.first == key }
28
- keys.each { |k| delete k }
35
+ keys.select { |k| k.sections.first == key }
36
+ .each { |k| delete k }
29
37
  end
30
38
 
31
39
  def keys
32
- @hash.keys.map { |k| Key[k] }
40
+ hash.keys.map { |k| Key[k] }
33
41
  end
34
42
 
35
43
  def synchronize(key, &block)
36
- @keys_mutex[key].synchronize(&block)
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
- def self.connections
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=#{@data_store_class} @data_store_connection_args=#{@data_store_connection_args} @key=#{@key}>"
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
- @data_store_class.connect(*@data_store_connection_args)
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[@key]
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 initialize(scope, *args)
9
+ def self.connect(*args)
10
+ new(*args)
11
+ end
12
+
13
+ def initialize(scope, options={})
10
14
  @scope = Key[scope]
11
- @redis = Redic.new(*args)
15
+ @options = options
12
16
  end
13
17
 
14
18
  def [](key)
15
- value = @redis.call 'GET', @scope[key]
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
- @redis.call 'SET', @scope[key], Marshal.dump(value)
27
+ redis.call! 'SET', scope[key], Marshal.dump(value)
24
28
  end
25
29
 
26
30
  def delete(key)
27
- @redis.call 'DEL', @scope[key]
31
+ redis.call! 'DEL', scope[key]
28
32
  end
29
33
 
30
34
  def delete_cascade(key)
31
- @redis.call 'DEL', @scope[key]
32
- @redis.call('KEYS', @scope[key]['*']).each { |k| @redis.call 'DEL', k }
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
- @redis.call('KEYS', @scope['*']).map { |k| Key[k].remove_first }
40
+ redis.call!('KEYS', scope['*']).map { |k| Key[k].remove_first }
37
41
  end
38
42
 
39
43
  def synchronize(key)
40
- while @redis.call('GETSET', @scope[key][LOCKED], LOCKED) == LOCKED
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
- @redis.call 'DEL', @scope[key][LOCKED]
49
+ redis.call! 'DEL', scope[key][LOCKED]
46
50
  end
47
51
 
48
52
  def connection_args
49
- [@scope, @redis.url]
53
+ [scope, options]
50
54
  end
51
55
 
52
- def self.connect(*args)
53
- new(*args)
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
- attr_reader :scope
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
- @data_store[@scope[key]]
20
+ data_store[scope[key]]
17
21
  end
18
22
 
19
23
  def []=(key, value)
20
- @data_store[@scope[key]] = value
24
+ data_store[scope[key]] = value
21
25
  end
22
26
 
23
27
  def delete(key)
24
- @data_store.delete @scope[key]
28
+ data_store.delete scope[key]
25
29
  end
26
30
 
27
31
  def delete_cascade
28
- @data_store.delete_cascade @scope
32
+ data_store.delete_cascade scope
29
33
  end
30
34
 
31
35
  def keys
32
- @data_store.keys.
33
- select { |k| k.start_with? @scope[''] }.
34
- map { |k| Key[k].remove_first @scope.sections.count }
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: @data_store.class,
45
- data_store_connection_args: @data_store.connection_args,
46
- scope: @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=#{@data_store} @scope=#{@scope}>"
56
+ "#<#{self.class} @data_store=#{data_store} @scope=#{scope}>"
58
57
  end
59
58
 
60
59
  end
@@ -1,12 +1,12 @@
1
1
  module Asynchronic
2
2
  class Environment
3
3
 
4
- attr_reader :queue_engine
5
- attr_reader :data_store
6
-
7
- def initialize(queue_engine, data_store)
4
+ attr_reader :queue_engine, :data_store, :notifier
5
+
6
+ def initialize(queue_engine, data_store, notifier)
8
7
  @queue_engine = queue_engine
9
8
  @data_store = data_store
9
+ @notifier = notifier
10
10
  end
11
11
 
12
12
  def queue(name)
@@ -14,7 +14,7 @@ module Asynchronic
14
14
  end
15
15
 
16
16
  def default_queue
17
- queue(queue_engine.default_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
@@ -1,9 +1,8 @@
1
1
  module Asynchronic
2
2
  class Error
3
3
 
4
- attr_reader :message
5
- attr_reader :backtrace
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 : []
@@ -18,6 +18,8 @@ module Asynchronic
18
18
  while @running
19
19
  processes = environment.processes
20
20
 
21
+ processes.each(&:abort_if_dead)
22
+
21
23
  conditions.each do |name, condition|
22
24
  Asynchronic.logger.info('Asynchronic') { "Running GC - #{name}" }
23
25
  begin