asynchronic 2.0.1 → 3.0.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: 1e125dac6480974894d48275d0015bb85410a9f4
4
- data.tar.gz: 07b4801fd659a5c0735ccac5dc24c5340363afa8
3
+ metadata.gz: fe064d93526974fae713afcd2d5fc4730cb65b94
4
+ data.tar.gz: f6ca2831a8d78323370116397d1cbb9d48c66837
5
5
  SHA512:
6
- metadata.gz: d68006d635184823ac8bcdc4669690b4778b295b4eb98c5b8b4375c0284be30db220ee87bf3f6f7fc932365adb26a3196116b6549ef3432fa2391ec1395f7363
7
- data.tar.gz: 630f880110f71041ab2bc18f5ad5258298b8f3d18560315569aa18ed8459986af20c3768cc3dc77992c82db3d2dce496125ad4931902cddd6ad11d4aaa8fe09e
6
+ metadata.gz: a6668033166027a2d54aca7b094f8f90642f4b180f01299e99099a62b47f3dffd871799773248c3ad0bb3a4127885029234cda953265782926b685a5596743b3
7
+ data.tar.gz: 86da5ba9d7207fae1d6f01f77f433f8ed7e3b6d6e64e3bec177755088ee0c844f5c8a768f942c905ce730d9ea1d6dfcf5caee4db1a83b20bbf4a7727a721ed04
@@ -19,6 +19,7 @@ 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
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'
@@ -27,6 +28,7 @@ Gem::Specification.new do |spec|
27
28
  spec.add_development_dependency 'rake', '~> 11.0'
28
29
  spec.add_development_dependency 'minitest', '~> 5.0', '< 5.11'
29
30
  spec.add_development_dependency 'minitest-great_expectations', '~> 0.0'
31
+ spec.add_development_dependency 'minitest-stub_any_instance', '~> 1.0'
30
32
  spec.add_development_dependency 'minitest-colorin', '~> 0.1'
31
33
  spec.add_development_dependency 'minitest-line', '~> 0.6'
32
34
  spec.add_development_dependency 'simplecov', '~> 0.12'
@@ -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,6 +19,7 @@ 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,7 +28,7 @@ module Asynchronic
26
28
  attr_config :connection_name, "HOST=#{Socket.gethostname},PID=#{::Process.pid}"
27
29
 
28
30
  def self.environment
29
- Environment.new queue_engine, data_store
31
+ Environment.new queue_engine, data_store, notifier
30
32
  end
31
33
 
32
34
  def self.[](pid)
@@ -1,12 +1,12 @@
1
1
  module Asynchronic
2
2
  class Environment
3
3
 
4
- attr_reader :queue_engine
5
- attr_reader :data_store
4
+ attr_reader :queue_engine, :data_store, :notifier
6
5
 
7
- def initialize(queue_engine, data_store)
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)
@@ -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
@@ -0,0 +1,30 @@
1
+ module Asynchronic
2
+ module Notifier
3
+ class Broadcaster
4
+
5
+ def initialize(options={})
6
+ options[:logger] ||= Asynchronic.logger
7
+ @broadcaster = ::Broadcaster.new options
8
+ end
9
+
10
+ def publish(pid, event, data=nil)
11
+ @broadcaster.publish DataStore::Key[pid][event], data
12
+ end
13
+
14
+ def subscribe(pid, event, &block)
15
+ @broadcaster.subscribe DataStore::Key[pid][event] do |data|
16
+ block.call data
17
+ end
18
+ end
19
+
20
+ def unsubscribe(subscription_id)
21
+ @broadcaster.unsubscribe subscription_id
22
+ end
23
+
24
+ def unsubscribe_all
25
+ @broadcaster.unsubscribe_all
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,33 @@
1
+ module Asynchronic
2
+ module Notifier
3
+ class InMemory
4
+
5
+ def publish(pid, event, data=nil)
6
+ subscriptions[DataStore::Key[pid][event]].each_value do |block|
7
+ block.call data
8
+ end
9
+ end
10
+
11
+ def subscribe(pid, event, &block)
12
+ SecureRandom.uuid.tap do |subscription_id|
13
+ subscriptions[DataStore::Key[pid][event]][subscription_id] = block
14
+ end
15
+ end
16
+
17
+ def unsubscribe(subscription_id)
18
+ subscriptions.each_value { |s| s.delete subscription_id }
19
+ end
20
+
21
+ def unsubscribe_all
22
+ subscriptions.clear
23
+ end
24
+
25
+ private
26
+
27
+ def subscriptions
28
+ @subscriptions ||= Hash.new { |h,k| h[k] = {} }
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -14,6 +14,7 @@ module Asynchronic
14
14
  ATTRIBUTE_NAMES = [:type, :name, :queue, :status, :dependencies, :data, :result, :error, :connection_name] | TIME_TRACKING_MAP.values.uniq
15
15
 
16
16
  CANCELED_ERROR_MESSAGE = 'Canceled'
17
+ DEAD_ERROR_MESSAGE = 'Process connection broken'
17
18
 
18
19
  attr_reader :id
19
20
 
@@ -51,6 +52,10 @@ module Asynchronic
51
52
  (running? && !connected?) || processes.any?(&:dead?)
52
53
  end
53
54
 
55
+ def abort_if_dead
56
+ abort! DEAD_ERROR_MESSAGE if dead?
57
+ end
58
+
54
59
  def destroy
55
60
  data_store.delete_cascade
56
61
  end
@@ -190,8 +195,12 @@ module Asynchronic
190
195
 
191
196
  def status=(status)
192
197
  Asynchronic.logger.info('Asynchronic') { "#{status.to_s.capitalize} #{type} (#{id})" }
198
+
193
199
  data_store[:status] = status
194
200
  data_store[TIME_TRACKING_MAP[status]] = Time.now if TIME_TRACKING_MAP.key? status
201
+
202
+ environment.notifier.publish id, :status_changed, status
203
+ environment.notifier.publish id, :finalized if finalized?
195
204
  end
196
205
 
197
206
  STATUSES.each do |status|
@@ -1,3 +1,3 @@
1
1
  module Asynchronic
2
- VERSION = '2.0.1'
2
+ VERSION = '3.0.0'
3
3
  end
@@ -26,7 +26,6 @@ module LazyValueExamples
26
26
  it 'Transparent proxy' do
27
27
  value = lazy_value :key
28
28
  data_store[:key] = 1
29
- value.must_be_instance_of Fixnum
30
29
  value.must_equal 1
31
30
  end
32
31
 
@@ -326,7 +326,7 @@ class NestedJobWithErrorInChildJob < Asynchronic::Job
326
326
  end
327
327
 
328
328
 
329
- class AbortQueuedAfertErrorJob < Asynchronic::Job
329
+ class AbortQueuedAfterErrorJob < Asynchronic::Job
330
330
  def call
331
331
  async Child_1
332
332
  async Child_2
@@ -3,6 +3,7 @@ require 'asynchronic'
3
3
  require 'minitest/autorun'
4
4
  require 'minitest/colorin'
5
5
  require 'minitest/great_expectations'
6
+ require 'minitest/stub_any_instance'
6
7
  require 'jobs'
7
8
  require 'expectations'
8
9
  require 'timeout'
@@ -1,12 +1,13 @@
1
1
  module LifeCycleExamples
2
2
 
3
- let(:env) { Asynchronic::Environment.new queue_engine, data_store }
3
+ let(:env) { Asynchronic::Environment.new queue_engine, data_store, notifier }
4
4
 
5
5
  let(:queue) { env.default_queue }
6
6
 
7
7
  after do
8
8
  data_store.clear
9
9
  queue_engine.clear
10
+ notifier.unsubscribe_all
10
11
  end
11
12
 
12
13
  def create(type, params={})
@@ -17,10 +18,41 @@ module LifeCycleExamples
17
18
 
18
19
  def execute(queue)
19
20
  process = env.load_process(queue.pop)
21
+
22
+ events = []
23
+ status_changed_id = notifier.subscribe(process.id, :status_changed) { |status| events << status }
24
+
25
+ is_finalized = false
26
+ finalized_id = notifier.subscribe(process.id, :finalized) { is_finalized = true }
27
+
20
28
  process.execute
29
+
21
30
  process.must_have_connection_name
22
31
  process.wont_be :dead?
23
32
  process.send(:connected?).must_be_true
33
+
34
+ with_retries do
35
+ events.last.must_equal process.status
36
+ process.finalized?.must_equal is_finalized
37
+ end
38
+
39
+ notifier.unsubscribe status_changed_id
40
+ notifier.unsubscribe finalized_id
41
+
42
+ status = process.status
43
+ process.abort_if_dead
44
+ process.status.must_equal status
45
+ end
46
+
47
+ def with_retries(&block)
48
+ Timeout.timeout(3) do
49
+ begin
50
+ block.call
51
+ rescue Minitest::Assertion
52
+ sleep 0.001
53
+ retry
54
+ end
55
+ end
24
56
  end
25
57
 
26
58
  it 'Basic' do
@@ -481,78 +513,78 @@ module LifeCycleExamples
481
513
  process.real_error.must_equal "Error in parent"
482
514
  end
483
515
 
484
- it 'Abort queued afert error' do
485
- process = create AbortQueuedAfertErrorJob
516
+ it 'Abort queued After error' do
517
+ process = create AbortQueuedAfterErrorJob
486
518
 
487
519
  process.enqueue
488
520
 
489
521
  execute queue
490
522
 
491
- process.full_status.must_equal 'AbortQueuedAfertErrorJob' => :waiting,
492
- 'AbortQueuedAfertErrorJob::Child_1' => :queued,
493
- 'AbortQueuedAfertErrorJob::Child_2' => :queued,
494
- 'AbortQueuedAfertErrorJob::Child_3' => :queued,
495
- 'AbortQueuedAfertErrorJob::Child_4' => :queued
523
+ process.full_status.must_equal 'AbortQueuedAfterErrorJob' => :waiting,
524
+ 'AbortQueuedAfterErrorJob::Child_1' => :queued,
525
+ 'AbortQueuedAfterErrorJob::Child_2' => :queued,
526
+ 'AbortQueuedAfterErrorJob::Child_3' => :queued,
527
+ 'AbortQueuedAfterErrorJob::Child_4' => :queued
496
528
 
497
529
  execute queue
498
530
 
499
- process.full_status.must_equal 'AbortQueuedAfertErrorJob' => :waiting,
500
- 'AbortQueuedAfertErrorJob::Child_1' => :waiting,
531
+ process.full_status.must_equal 'AbortQueuedAfterErrorJob' => :waiting,
532
+ 'AbortQueuedAfterErrorJob::Child_1' => :waiting,
501
533
  'Child_1_1' => :queued,
502
534
  'Child_1_2' => :queued,
503
- 'AbortQueuedAfertErrorJob::Child_2' => :queued,
504
- 'AbortQueuedAfertErrorJob::Child_3' => :queued,
505
- 'AbortQueuedAfertErrorJob::Child_4' => :queued
535
+ 'AbortQueuedAfterErrorJob::Child_2' => :queued,
536
+ 'AbortQueuedAfterErrorJob::Child_3' => :queued,
537
+ 'AbortQueuedAfterErrorJob::Child_4' => :queued
506
538
 
507
539
  execute queue
508
540
 
509
- process.full_status.must_equal 'AbortQueuedAfertErrorJob' => :waiting,
510
- 'AbortQueuedAfertErrorJob::Child_1' => :waiting,
541
+ process.full_status.must_equal 'AbortQueuedAfterErrorJob' => :waiting,
542
+ 'AbortQueuedAfterErrorJob::Child_1' => :waiting,
511
543
  'Child_1_1' => :queued,
512
544
  'Child_1_2' => :queued,
513
- 'AbortQueuedAfertErrorJob::Child_2' => :completed,
514
- 'AbortQueuedAfertErrorJob::Child_3' => :queued,
515
- 'AbortQueuedAfertErrorJob::Child_4' => :queued
545
+ 'AbortQueuedAfterErrorJob::Child_2' => :completed,
546
+ 'AbortQueuedAfterErrorJob::Child_3' => :queued,
547
+ 'AbortQueuedAfterErrorJob::Child_4' => :queued
516
548
 
517
549
  execute queue
518
550
 
519
- process.full_status.must_equal 'AbortQueuedAfertErrorJob' => :aborted,
520
- 'AbortQueuedAfertErrorJob::Child_1' => :waiting,
551
+ process.full_status.must_equal 'AbortQueuedAfterErrorJob' => :aborted,
552
+ 'AbortQueuedAfterErrorJob::Child_1' => :waiting,
521
553
  'Child_1_1' => :queued,
522
554
  'Child_1_2' => :queued,
523
- 'AbortQueuedAfertErrorJob::Child_2' => :completed,
524
- 'AbortQueuedAfertErrorJob::Child_3' => :aborted,
525
- 'AbortQueuedAfertErrorJob::Child_4' => :queued
555
+ 'AbortQueuedAfterErrorJob::Child_2' => :completed,
556
+ 'AbortQueuedAfterErrorJob::Child_3' => :aborted,
557
+ 'AbortQueuedAfterErrorJob::Child_4' => :queued
526
558
 
527
559
  execute queue
528
560
 
529
- process.full_status.must_equal 'AbortQueuedAfertErrorJob' => :aborted,
530
- 'AbortQueuedAfertErrorJob::Child_1' => :waiting,
561
+ process.full_status.must_equal 'AbortQueuedAfterErrorJob' => :aborted,
562
+ 'AbortQueuedAfterErrorJob::Child_1' => :waiting,
531
563
  'Child_1_1' => :queued,
532
564
  'Child_1_2' => :queued,
533
- 'AbortQueuedAfertErrorJob::Child_2' => :completed,
534
- 'AbortQueuedAfertErrorJob::Child_3' => :aborted,
535
- 'AbortQueuedAfertErrorJob::Child_4' => :aborted
565
+ 'AbortQueuedAfterErrorJob::Child_2' => :completed,
566
+ 'AbortQueuedAfterErrorJob::Child_3' => :aborted,
567
+ 'AbortQueuedAfterErrorJob::Child_4' => :aborted
536
568
 
537
569
  execute queue
538
570
 
539
- process.full_status.must_equal 'AbortQueuedAfertErrorJob' => :aborted,
540
- 'AbortQueuedAfertErrorJob::Child_1' => :aborted,
571
+ process.full_status.must_equal 'AbortQueuedAfterErrorJob' => :aborted,
572
+ 'AbortQueuedAfterErrorJob::Child_1' => :aborted,
541
573
  'Child_1_1' => :aborted,
542
574
  'Child_1_2' => :queued,
543
- 'AbortQueuedAfertErrorJob::Child_2' => :completed,
544
- 'AbortQueuedAfertErrorJob::Child_3' => :aborted,
545
- 'AbortQueuedAfertErrorJob::Child_4' => :aborted
575
+ 'AbortQueuedAfterErrorJob::Child_2' => :completed,
576
+ 'AbortQueuedAfterErrorJob::Child_3' => :aborted,
577
+ 'AbortQueuedAfterErrorJob::Child_4' => :aborted
546
578
 
547
579
  execute queue
548
580
 
549
- process.full_status.must_equal 'AbortQueuedAfertErrorJob' => :aborted,
550
- 'AbortQueuedAfertErrorJob::Child_1' => :aborted,
581
+ process.full_status.must_equal 'AbortQueuedAfterErrorJob' => :aborted,
582
+ 'AbortQueuedAfterErrorJob::Child_1' => :aborted,
551
583
  'Child_1_1' => :aborted,
552
584
  'Child_1_2' => :aborted,
553
- 'AbortQueuedAfertErrorJob::Child_2' => :completed,
554
- 'AbortQueuedAfertErrorJob::Child_3' => :aborted,
555
- 'AbortQueuedAfertErrorJob::Child_4' => :aborted
585
+ 'AbortQueuedAfterErrorJob::Child_2' => :completed,
586
+ 'AbortQueuedAfterErrorJob::Child_3' => :aborted,
587
+ 'AbortQueuedAfterErrorJob::Child_4' => :aborted
556
588
 
557
589
  process.real_error.must_equal 'Forced error'
558
590
  end
@@ -617,36 +649,44 @@ module LifeCycleExamples
617
649
  process_2.enqueue
618
650
  execute queue
619
651
 
652
+ process_3 = create BasicJob
653
+
620
654
  pid_1 = process_1.id
621
655
  pid_2 = process_2.id
656
+ pid_3 = process_3.id
622
657
 
623
658
  process_1.must_be_completed
624
659
  process_2.must_be_waiting
660
+ process_3.must_be_pending
625
661
 
626
662
  data_store.keys.select { |k| k.start_with? pid_1 }.count.must_equal 53
627
663
  data_store.keys.select { |k| k.start_with? pid_2 }.count.must_equal 38
664
+ data_store.keys.select { |k| k.start_with? pid_3 }.count.must_equal 7
628
665
 
629
666
  gc = Asynchronic::GarbageCollector.new env, 0.001
630
667
 
631
- gc.add_condition('Completed', &:completed?)
668
+ gc.add_condition('Finalized', &:finalized?)
632
669
  gc.add_condition('Waiting', &:waiting?)
633
670
  gc.add_condition('Exception') { raise 'Invalid condition' }
634
671
 
635
- gc.conditions_names.must_equal ['Completed', 'Waiting', 'Exception']
672
+ gc.conditions_names.must_equal ['Finalized', 'Waiting', 'Exception']
636
673
 
637
674
  gc.remove_condition 'Waiting'
638
675
 
639
- gc.conditions_names.must_equal ['Completed', 'Exception']
676
+ gc.conditions_names.must_equal ['Finalized', 'Exception']
640
677
 
641
678
  Thread.new do
642
679
  sleep 0.01
643
680
  gc.stop
644
681
  end
645
682
 
646
- gc.start
683
+ Asynchronic::Process.stub_any_instance(:dead?, -> { id == pid_3 }) do
684
+ gc.start
685
+ end
647
686
 
648
687
  data_store.keys.select { |k| k.start_with? pid_1 }.count.must_equal 0
649
688
  data_store.keys.select { |k| k.start_with? pid_2 }.count.must_equal 38
689
+ data_store.keys.select { |k| k.start_with? pid_3 }.count.must_equal 0
650
690
  end
651
691
 
652
692
  it 'Before finalize hook when completed' do
@@ -5,6 +5,7 @@ describe Asynchronic::Process, 'Life cycle - InMemory' do
5
5
 
6
6
  let(:queue_engine) { Asynchronic::QueueEngine::InMemory.new }
7
7
  let(:data_store) { Asynchronic::DataStore::InMemory.new }
8
+ let(:notifier) { Asynchronic::Notifier::InMemory.new }
8
9
 
9
10
  include LifeCycleExamples
10
11
 
@@ -5,6 +5,7 @@ describe Asynchronic::Process, 'Life cycle - Redis' do
5
5
 
6
6
  let(:queue_engine) { Asynchronic::QueueEngine::Ost.new }
7
7
  let(:data_store) { Asynchronic::DataStore::Redis.new :asynchronic_test }
8
+ let(:notifier) { Asynchronic::Notifier::Broadcaster.new }
8
9
 
9
10
  include LifeCycleExamples
10
11
 
@@ -5,6 +5,7 @@ describe Asynchronic::Worker, 'InMemory' do
5
5
 
6
6
  let(:queue_engine) { Asynchronic::QueueEngine::InMemory.new }
7
7
  let(:data_store) { Asynchronic::DataStore::InMemory.new }
8
+ let(:notifier) { Asynchronic::Notifier::InMemory.new }
8
9
 
9
10
  include WorkerExamples
10
11
 
@@ -5,6 +5,7 @@ describe Asynchronic::Worker, 'Redis' do
5
5
 
6
6
  let(:queue_engine) { Asynchronic::QueueEngine::Ost.new }
7
7
  let(:data_store) { Asynchronic::DataStore::Redis.new :asynchronic_test}
8
+ let(:notifier) { Asynchronic::Notifier::Broadcaster.new }
8
9
 
9
10
  after do
10
11
  data_store.clear
@@ -1,6 +1,6 @@
1
1
  module WorkerExamples
2
2
 
3
- let(:env) { Asynchronic::Environment.new queue_engine, data_store }
3
+ let(:env) { Asynchronic::Environment.new queue_engine, data_store, notifier }
4
4
  let(:queue_name) { :test_worker }
5
5
  let(:queue) { env.queue queue_name }
6
6
 
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: 2.0.1
4
+ version: 3.0.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: 2019-06-20 00:00:00.000000000 Z
11
+ date: 2019-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ost
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: broadcaster
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: class_config
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -128,6 +142,20 @@ dependencies:
128
142
  - - "~>"
129
143
  - !ruby/object:Gem::Version
130
144
  version: '0.0'
145
+ - !ruby/object:Gem::Dependency
146
+ name: minitest-stub_any_instance
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '1.0'
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '1.0'
131
159
  - !ruby/object:Gem::Dependency
132
160
  name: minitest-colorin
133
161
  requirement: !ruby/object:Gem::Requirement
@@ -229,6 +257,8 @@ files:
229
257
  - lib/asynchronic/error.rb
230
258
  - lib/asynchronic/garbage_collector.rb
231
259
  - lib/asynchronic/job.rb
260
+ - lib/asynchronic/notifier/broadcaster.rb
261
+ - lib/asynchronic/notifier/in_memory.rb
232
262
  - lib/asynchronic/process.rb
233
263
  - lib/asynchronic/queue_engine/in_memory.rb
234
264
  - lib/asynchronic/queue_engine/ost.rb