collective 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +13 -0
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/Gemfile +16 -0
- data/Guardfile +6 -0
- data/README +7 -0
- data/Rakefile +11 -0
- data/bin/collective +37 -0
- data/collective.gemspec +23 -0
- data/demo/demo +36 -0
- data/demo/demo.rb +30 -0
- data/demo/demo3 +36 -0
- data/demo/job1.rb +31 -0
- data/demo/job2.rb +42 -0
- data/demo/job3.rb +44 -0
- data/demo/populate.rb +22 -0
- data/lib/collective.rb +52 -0
- data/lib/collective/checker.rb +51 -0
- data/lib/collective/configuration.rb +219 -0
- data/lib/collective/idler.rb +81 -0
- data/lib/collective/key.rb +48 -0
- data/lib/collective/lifecycle_observer.rb +25 -0
- data/lib/collective/log.rb +29 -0
- data/lib/collective/messager.rb +218 -0
- data/lib/collective/mocks/storage.rb +108 -0
- data/lib/collective/monitor.rb +58 -0
- data/lib/collective/policy.rb +60 -0
- data/lib/collective/pool.rb +180 -0
- data/lib/collective/redis/storage.rb +142 -0
- data/lib/collective/registry.rb +123 -0
- data/lib/collective/squiggly.rb +20 -0
- data/lib/collective/utilities/airbrake_observer.rb +26 -0
- data/lib/collective/utilities/hoptoad_observer.rb +26 -0
- data/lib/collective/utilities/log_observer.rb +40 -0
- data/lib/collective/utilities/observeable.rb +18 -0
- data/lib/collective/utilities/observer_base.rb +59 -0
- data/lib/collective/utilities/process.rb +82 -0
- data/lib/collective/utilities/signal_hook.rb +47 -0
- data/lib/collective/utilities/storage_base.rb +41 -0
- data/lib/collective/version.rb +3 -0
- data/lib/collective/worker.rb +161 -0
- data/spec/checker_spec.rb +20 -0
- data/spec/configuration_spec.rb +24 -0
- data/spec/helper.rb +33 -0
- data/spec/idler_spec.rb +58 -0
- data/spec/key_spec.rb +41 -0
- data/spec/messager_spec.rb +131 -0
- data/spec/mocks/storage_spec.rb +108 -0
- data/spec/monitor_spec.rb +15 -0
- data/spec/policy_spec.rb +43 -0
- data/spec/pool_spec.rb +119 -0
- data/spec/redis/storage_spec.rb +133 -0
- data/spec/registry_spec.rb +52 -0
- data/spec/support/jobs.rb +58 -0
- data/spec/support/redis.rb +22 -0
- data/spec/support/timing.rb +32 -0
- data/spec/utilities/observer_base_spec.rb +50 -0
- data/spec/utilities/process_spec.rb +17 -0
- data/spec/worker_spec.rb +121 -0
- data/unused/times.rb +45 -0
- metadata +148 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
class Collective::Utilities::StorageBase
|
4
|
+
|
5
|
+
module Resolver
|
6
|
+
# resolution is as so:
|
7
|
+
# nil, :mock => :mock
|
8
|
+
# :redis => redis://127.0.0.1:6379/1
|
9
|
+
# string => CLASS.new
|
10
|
+
# CLASS => CLASS.new
|
11
|
+
# PROC => yield (recursive)
|
12
|
+
# [ ARRAY ] => first, *args (recursive)
|
13
|
+
def resolve( storage, *args )
|
14
|
+
storage ||= :mock
|
15
|
+
case
|
16
|
+
when storage.respond_to?(:call)
|
17
|
+
resolve(storage.call(*args))
|
18
|
+
else
|
19
|
+
case storage
|
20
|
+
when :mock
|
21
|
+
resolve( Collective::Mocks::Storage, *args )
|
22
|
+
when :redis
|
23
|
+
resolve( Collective::Redis::Storage, *args )
|
24
|
+
when Class
|
25
|
+
storage.new(*args)
|
26
|
+
when String
|
27
|
+
resolve( Collective.resolve_class(storage), *args )
|
28
|
+
when Array
|
29
|
+
args = storage.dup + args
|
30
|
+
storage = args.shift
|
31
|
+
resolve( storage, *args )
|
32
|
+
else
|
33
|
+
return storage
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end # resolve
|
37
|
+
end # Resolver
|
38
|
+
|
39
|
+
extend Resolver
|
40
|
+
|
41
|
+
end # Collective::Utilities::StorageBase
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
=begin
|
4
|
+
|
5
|
+
A Worker is a forked process which runs jobs.
|
6
|
+
Jobs are short lived and run repeatedly.
|
7
|
+
|
8
|
+
=end
|
9
|
+
|
10
|
+
class Collective::Worker
|
11
|
+
|
12
|
+
include Collective::Utilities::Observeable
|
13
|
+
|
14
|
+
# forks a new process
|
15
|
+
# creates a new instance of the job class
|
16
|
+
# runs a loop which calls the job
|
17
|
+
def self.spawn( prototype_job, options = {} )
|
18
|
+
policy = options[:policy] || Collective::Policy.resolve
|
19
|
+
name = options[:name] || policy.name || prototype_job.to_s
|
20
|
+
storage = policy.storage
|
21
|
+
registry = options[:registry] || Collective::Registry.new( name, storage )
|
22
|
+
|
23
|
+
foptions = { stdout: "/tmp/debug.log" }
|
24
|
+
Collective::Utilities::Process.fork_and_detach( foptions ) do
|
25
|
+
if after_forks = policy.after_forks then
|
26
|
+
after_forks.each { |af| af.call }
|
27
|
+
end
|
28
|
+
# $0 = "$0 #{name}"
|
29
|
+
worker = new( prototype_job, options )
|
30
|
+
trap("TERM") { worker.quit! }
|
31
|
+
worker.run
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
attr :job
|
36
|
+
attr :name
|
37
|
+
attr :policy
|
38
|
+
attr :registry
|
39
|
+
attr :state
|
40
|
+
attr :storage
|
41
|
+
attr :worker_expire
|
42
|
+
attr :worker_jobs
|
43
|
+
|
44
|
+
# @param options[:name] is optional
|
45
|
+
# @param options[:policy] is optional
|
46
|
+
# @param options[:registry] is optional
|
47
|
+
def initialize( prototype_job, options = {} )
|
48
|
+
@policy = options[:policy] || Collective::Policy.resolve
|
49
|
+
@name = options[:name] || policy.name || prototype_job.to_s
|
50
|
+
@storage = policy.storage
|
51
|
+
@registry = options[:registry] || Collective::Registry.new( name, storage )
|
52
|
+
@job = Collective::Idler.new( resolve_job( prototype_job ), min_sleep: policy.worker_idle_min_sleep, max_sleep: policy.worker_idle_max_sleep )
|
53
|
+
|
54
|
+
# type checks
|
55
|
+
policy.pool_min_workers
|
56
|
+
registry.workers
|
57
|
+
|
58
|
+
# post-fork processing
|
59
|
+
storage.reconnect_after_fork
|
60
|
+
registry.reconnect_after_fork
|
61
|
+
|
62
|
+
# set up observers
|
63
|
+
policy.observers.each do |observer|
|
64
|
+
o = Collective::Utilities::ObserverBase.resolve(observer)
|
65
|
+
add_observer(o)
|
66
|
+
end
|
67
|
+
|
68
|
+
# manage the registry via an observer
|
69
|
+
add_observer( Collective::LifecycleObserver.new( key, registry ) )
|
70
|
+
end
|
71
|
+
|
72
|
+
def run()
|
73
|
+
@state = :running
|
74
|
+
@worker_jobs = 0
|
75
|
+
@worker_expire = Time.now + policy.worker_max_lifetime
|
76
|
+
|
77
|
+
context = { worker: self }
|
78
|
+
with_start_and_stop do
|
79
|
+
while running? do
|
80
|
+
with_quitting_checks do
|
81
|
+
with_heartbeat do
|
82
|
+
job.call(context)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def quit!()
|
90
|
+
@state = :quitting
|
91
|
+
end
|
92
|
+
|
93
|
+
def running?
|
94
|
+
state == :running
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_s
|
98
|
+
%Q[Worker(#{key})]
|
99
|
+
end
|
100
|
+
|
101
|
+
# the key is a constant string which uniquely identifies this worker
|
102
|
+
# WARNING this would be invalidated if we forked or set this before forking
|
103
|
+
def key
|
104
|
+
@key ||= Collective::Key.new( name, Process.pid )
|
105
|
+
end
|
106
|
+
|
107
|
+
def mq
|
108
|
+
@mq ||= Collective::Messager.new( storage, my_address: key )
|
109
|
+
end
|
110
|
+
|
111
|
+
# ----------------------------------------------------------------------------
|
112
|
+
protected
|
113
|
+
# ----------------------------------------------------------------------------
|
114
|
+
|
115
|
+
def with_start_and_stop(&block)
|
116
|
+
notify :worker_started
|
117
|
+
begin
|
118
|
+
yield
|
119
|
+
ensure
|
120
|
+
notify :worker_stopped
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def with_quitting_checks(&block)
|
125
|
+
yield
|
126
|
+
ensure
|
127
|
+
@worker_jobs += 1
|
128
|
+
quit! if policy.worker_max_jobs <= worker_jobs
|
129
|
+
quit! if worker_expire <= Time.now
|
130
|
+
end
|
131
|
+
|
132
|
+
def with_heartbeat(&block)
|
133
|
+
begin
|
134
|
+
yield
|
135
|
+
rescue => x
|
136
|
+
notify :job_error, x
|
137
|
+
ensure
|
138
|
+
notify :worker_heartbeat
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def resolve_job( job_factory )
|
143
|
+
raise Collective::ConfigurationError if ! job_factory
|
144
|
+
|
145
|
+
case
|
146
|
+
when job_factory.respond_to?(:call)
|
147
|
+
job_factory
|
148
|
+
when job_factory.respond_to?(:new)
|
149
|
+
context = { worker: self }
|
150
|
+
resolve_job(job_factory.new(context))
|
151
|
+
else
|
152
|
+
case job_factory
|
153
|
+
when String, Symbol
|
154
|
+
resolve_job(Collective.resolve_class(job_factory.to_s))
|
155
|
+
else
|
156
|
+
raise Collective::ConfigurationError, "Unknown kind of job #{job_factory.inspect}"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end # Collective::Worker
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require "helper"
|
4
|
+
require "collective/checker"
|
5
|
+
|
6
|
+
describe Checker do
|
7
|
+
|
8
|
+
it "does something" do
|
9
|
+
activity_count = 192
|
10
|
+
last_time = Time.now - 3600
|
11
|
+
c = Checker.new activity_count, last_time
|
12
|
+
|
13
|
+
c.check?.should_not be_false
|
14
|
+
c.estimated_delay.should be >= 1.0
|
15
|
+
c.estimated_delay.should be < 8.0
|
16
|
+
|
17
|
+
c.checked 12
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require "helper"
|
4
|
+
|
5
|
+
describe Collective::Configuration do
|
6
|
+
|
7
|
+
it "should parse command-line switches" do
|
8
|
+
c = Collective::Configuration.parse %w(--dry-run --env the_env --name a_name --chdir .)
|
9
|
+
c.env.should eq("the_env")
|
10
|
+
c.name.should eq("a_name")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should parse the DSL" do
|
14
|
+
script = <<-EOT.gsub(/^ +/,'')
|
15
|
+
set_env "the_env"
|
16
|
+
set_name "a_name"
|
17
|
+
chdir "."
|
18
|
+
EOT
|
19
|
+
c = Collective::Configuration.parse ["--dry-run", "--script", script]
|
20
|
+
c.env.should eq("the_env")
|
21
|
+
c.name.should eq("a_name")
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/spec/helper.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
=begin
|
4
|
+
@see https://www.relishapp.com/rspec/rspec-core/docs/hooks/around-hooks
|
5
|
+
@see https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/be-matchers
|
6
|
+
@see https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/equality-matchers
|
7
|
+
@see https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/operator-matchers
|
8
|
+
@see https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/predicate-matchers
|
9
|
+
=end
|
10
|
+
|
11
|
+
require "bundler/setup" # set up gem paths
|
12
|
+
require "ruby-debug" # because sometimes you need it
|
13
|
+
|
14
|
+
#require "simplecov" # code coverage
|
15
|
+
#SimpleCov.start # must be loaded before our own code
|
16
|
+
|
17
|
+
require "collective" # load this gem
|
18
|
+
require "support/jobs" # simple helpers for testing
|
19
|
+
require "support/redis" # simple helpers for testing
|
20
|
+
require "support/timing" # simple helpers for testing
|
21
|
+
|
22
|
+
RSpec.configure do |spec|
|
23
|
+
# @see https://www.relishapp.com/rspec/rspec-core/docs/helper-methods/define-helper-methods-in-a-module
|
24
|
+
spec.include RedisClient, redis: true
|
25
|
+
spec.include Timing, time: true
|
26
|
+
spec.include Collective::Idler::Utilities
|
27
|
+
|
28
|
+
# nuke the Redis database around each run
|
29
|
+
# @see https://www.relishapp.com/rspec/rspec-core/docs/hooks/around-hooks
|
30
|
+
spec.around( :each, redis: true ) do |example|
|
31
|
+
with_clean_redis { example.run }
|
32
|
+
end
|
33
|
+
end
|
data/spec/idler_spec.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require "helper"
|
4
|
+
|
5
|
+
describe Collective::Idler do
|
6
|
+
|
7
|
+
it "should accept a proc" do
|
8
|
+
idler = Collective::Idler.new() { true }
|
9
|
+
expect { idler.call }.to_not raise_error
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should accept a lambda" do
|
13
|
+
job = ->() { true }
|
14
|
+
idler = Collective::Idler.new(job)
|
15
|
+
expect { idler.call }.to_not raise_error
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should accept an object with an interface" do
|
19
|
+
idler = Collective::Idler.new( TrueJob.new )
|
20
|
+
expect { idler.call }.to_not raise_error
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should refuse non-callable jobs" do
|
24
|
+
fake_job = Object.new
|
25
|
+
expect { Collective::Idler.new(fake_job) }.to raise_error
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "when dealing with sleep times", time: true do
|
29
|
+
|
30
|
+
it "should not run idle tasks too much" do
|
31
|
+
count = 0
|
32
|
+
Collective::Idler.wait_until { count += 1; false }
|
33
|
+
count.should be <= 10
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should sleep after a failure" do
|
37
|
+
job = ->() { false }
|
38
|
+
idler = Collective::Idler.new( job, min_sleep: 0.125 )
|
39
|
+
time { idler.call }.should be >= 0.125
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should not sleep after a success" do
|
43
|
+
result = false
|
44
|
+
job = ->() { result }
|
45
|
+
idler = Collective::Idler.new( job, min_sleep: 0.125 )
|
46
|
+
idler.call
|
47
|
+
time { result = true; idler.call }.should be < 0.1
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should not sleep too long" do
|
51
|
+
job = ->() { false }
|
52
|
+
idler = Collective::Idler.new( job, min_sleep: 0.125 )
|
53
|
+
time { idler.call }.should be < 0.25
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
data/spec/key_spec.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require "helper"
|
4
|
+
|
5
|
+
describe Collective::Key do
|
6
|
+
|
7
|
+
it "can make keys" do
|
8
|
+
name = "processor"
|
9
|
+
pid = 1234
|
10
|
+
host = "foo.example.com"
|
11
|
+
Collective::Key.new( name, pid, host ).to_s.should eq("processor-1234@foo.example.com")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "can parse keys" do
|
15
|
+
Collective::Key.parse("processor-1234@foo.example.com").should eq(Collective::Key.new( "processor", "1234", "foo.example.com" ))
|
16
|
+
Collective::Key.parse("test-job-1234@foo.example.com").should eq(Collective::Key.new( "test-job", "1234", "foo.example.com" ))
|
17
|
+
end
|
18
|
+
|
19
|
+
it "can compare to another key" do
|
20
|
+
key1 = Collective::Key.new "Alpha", 1234, "example.com"
|
21
|
+
key2 = Collective::Key.new "Alpha", 1234, "example.com"
|
22
|
+
key1.should eq(key2)
|
23
|
+
|
24
|
+
key1 = Collective::Key.new "Alpha", 1234, "example.com"
|
25
|
+
key2 = Collective::Key.new "Alpha", "1234", "example.com"
|
26
|
+
key1.should eq(key2)
|
27
|
+
|
28
|
+
key1 = Collective::Key.new "Alpha", 1234, "example.com"
|
29
|
+
key2 = Collective::Key.new "Beta", 1234, "example.com"
|
30
|
+
key1.should_not eq(key2)
|
31
|
+
|
32
|
+
key1 = Collective::Key.new "Alpha", 1234, "example.com"
|
33
|
+
key2 = Collective::Key.new "Alpha", 2345, "example.com"
|
34
|
+
key1.should_not eq(key2)
|
35
|
+
|
36
|
+
key1 = Collective::Key.new "Alpha", 1234, "example.com"
|
37
|
+
key2 = Collective::Key.new "Alpha", 1234, "example.org"
|
38
|
+
key1.should_not eq(key2)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require "helper"
|
4
|
+
|
5
|
+
describe Collective::Messager, redis: true do
|
6
|
+
|
7
|
+
before do
|
8
|
+
@a = "me@example.com"
|
9
|
+
@b = "you@example.com"
|
10
|
+
end
|
11
|
+
|
12
|
+
it "can take to_address initially or per message" do
|
13
|
+
storage = Collective::Mocks::Storage.new
|
14
|
+
|
15
|
+
a = Collective::Messager.new( storage, my_address: @a )
|
16
|
+
expect { a.send "Hello" }.to raise_exception
|
17
|
+
expect { a.send "Hello", to: @b }.to_not raise_exception
|
18
|
+
|
19
|
+
b = Collective::Messager.new( storage, my_address: @a, to_address: @b )
|
20
|
+
expect { b.send "Hello" }.to_not raise_exception
|
21
|
+
end
|
22
|
+
|
23
|
+
it "the message id varies with the source, content and timestamp" do
|
24
|
+
storage = Collective::Mocks::Storage.new
|
25
|
+
a = Collective::Messager.new( storage, my_address: @a, to_address: @b )
|
26
|
+
b = Collective::Messager.new( storage, my_address: @b, to_address: @a )
|
27
|
+
now = 1234567890
|
28
|
+
|
29
|
+
id1 = a.send "Hello", at: now
|
30
|
+
id3 = a.send "Hello", at: now+1
|
31
|
+
id4 = a.send "Goodbye", at: now
|
32
|
+
id5 = b.send "Hello", at: now
|
33
|
+
|
34
|
+
id3.should_not eq(id1)
|
35
|
+
id4.should_not eq(id1)
|
36
|
+
id5.should_not eq(id1)
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "sending and receiving messages" do
|
40
|
+
|
41
|
+
it "can match against a string" do
|
42
|
+
storage = Collective::Mocks::Storage.new
|
43
|
+
a = Collective::Messager.new( storage, my_address: @a )
|
44
|
+
b = Collective::Messager.new( storage, my_address: @b )
|
45
|
+
b.expect("Hello") { |message| false }
|
46
|
+
a.send "Hello", to: @b
|
47
|
+
expect { b.receive }.to_not raise_exception
|
48
|
+
end
|
49
|
+
|
50
|
+
it "can match against a regexp" do
|
51
|
+
storage = Collective::Mocks::Storage.new
|
52
|
+
a = Collective::Messager.new( storage, my_address: @a )
|
53
|
+
b = Collective::Messager.new( storage, my_address: @b )
|
54
|
+
b.expect(/ello/) { |message| false }
|
55
|
+
a.send "Hello", to: @b
|
56
|
+
expect { b.receive }.to_not raise_exception
|
57
|
+
end
|
58
|
+
|
59
|
+
it "returns true if it got a message" do
|
60
|
+
storage = Collective::Mocks::Storage.new
|
61
|
+
a = Collective::Messager.new( storage, my_address: @a )
|
62
|
+
b = Collective::Messager.new( storage, my_address: @b )
|
63
|
+
b.expect(//) { |message| false }
|
64
|
+
a.receive.should eq(false)
|
65
|
+
a.send "Hello", to: @b
|
66
|
+
b.receive.should eq(true)
|
67
|
+
b.receive.should eq(false)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "can send and receive messages" do
|
71
|
+
storage = Collective::Mocks::Storage.new
|
72
|
+
a = Collective::Messager.new( storage, my_address: @a )
|
73
|
+
b = Collective::Messager.new( storage, my_address: @b )
|
74
|
+
|
75
|
+
callback = double("callback")
|
76
|
+
callback.should_receive(:call).with(anything)
|
77
|
+
|
78
|
+
reply_to_id = nil
|
79
|
+
|
80
|
+
b.expect("Hello") { |message|
|
81
|
+
message.body.should eq("Hello")
|
82
|
+
message.from.should eq(@a)
|
83
|
+
reply_to_id = message.id
|
84
|
+
b.reply( "Goodbye", to: message )
|
85
|
+
}
|
86
|
+
|
87
|
+
a.expect("Goodbye") { |message|
|
88
|
+
message.body.should eq("Goodbye")
|
89
|
+
message.from.should eq(@b)
|
90
|
+
message.id.should_not be_nil
|
91
|
+
message.reply_to_id.should eq(reply_to_id)
|
92
|
+
callback.call(message)
|
93
|
+
}
|
94
|
+
|
95
|
+
a.send "Hello", to: @b
|
96
|
+
b.receive
|
97
|
+
a.receive
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "when working with multiple processes", redis: true do
|
103
|
+
|
104
|
+
it "can send a message between processes" do
|
105
|
+
storage = Collective::Redis::Storage.new(redis)
|
106
|
+
me = Collective::Messager.new( storage, my_address: @a )
|
107
|
+
|
108
|
+
ok = false
|
109
|
+
me.expect("Goodbye") do |message|
|
110
|
+
ok = true
|
111
|
+
end
|
112
|
+
|
113
|
+
Collective::Utilities::Process.fork_and_detach do
|
114
|
+
redis.client.disconnect
|
115
|
+
me = Collective::Messager.new( storage, my_address: @b )
|
116
|
+
ok = false
|
117
|
+
me.expect("Hello") do |message|
|
118
|
+
me.reply "Goodbye", to: message
|
119
|
+
ok = true
|
120
|
+
end
|
121
|
+
wait_until { me.receive; ok }
|
122
|
+
end
|
123
|
+
|
124
|
+
me.send "Hello", to: @b
|
125
|
+
wait_until { me.receive }
|
126
|
+
ok.should be_true
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|