robinhood 0.0.1.pre → 0.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 +7 -0
- data/.ruby-version +1 -1
- data/README.md +14 -5
- data/lib/robinhood/dsl.rb +46 -0
- data/lib/robinhood/mutex.rb +7 -0
- data/lib/robinhood/process.rb +48 -8
- data/lib/robinhood/runtime.rb +77 -0
- data/lib/robinhood/version.rb +1 -1
- data/lib/robinhood.rb +76 -7
- data/spec/acceptance/dsl_spec.rb +45 -0
- data/spec/lib/robinhood/dsl_spec.rb +32 -0
- data/spec/lib/robinhood/process_spec.rb +36 -0
- data/spec/lib/robinhood/runtime_spec.rb +43 -0
- data/spec/spec_helper.rb +4 -4
- metadata +64 -91
- data/lib/robinhood/setup.rb +0 -54
- data/spec/acceptance/setup_spec.rb +0 -46
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 913bb9ef8276fd39f9c3ba6f1ad43548091601d9
|
4
|
+
data.tar.gz: fbb34da42c7ccc99289a41f5fc38495a9360f373
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e09bdea365ad31009265edf0cf7607a0ca47c4b26464a646f35d94a1f556c196c9e3f2fec91297a41968bf1f9a0379adfdfc57adc6ca84b49efc8550a1525de5
|
7
|
+
data.tar.gz: 0e1aaae54ee25b24b35addf173a1f26c94bdd04cf9314ec5fade00e944e61ca106196dafd57caca42b700b415d5bb4e6af6a93fb35e0f57e174db61aec7d1d0c
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2.0.0-p247
|
data/README.md
CHANGED
@@ -1,30 +1,39 @@
|
|
1
|
-
# Robinhood
|
1
|
+
# Robinhood [](https://www.travis-ci.org/codegram/robinhood)
|
2
2
|
|
3
3
|
Robinhood is a DSL for constructing iteration-based synchronous processes
|
4
4
|
(aka can't be run as jobs) in a distributed manner.
|
5
5
|
|
6
|
+
In other words: Robs responsibilities from richer processes and gives them to
|
7
|
+
the poor.
|
8
|
+
|
6
9
|
It leverages celluloid actors for each process and uses Redis as a locking
|
7
10
|
mechanism to ensure the process is run in a single server.
|
8
11
|
|
9
12
|
## Usage
|
10
13
|
|
11
14
|
```ruby
|
12
|
-
Robinhood.
|
13
|
-
|
15
|
+
Robinhood.define do
|
16
|
+
redis{ Redis.new(:host => "10.0.1.1", :port => 6380) }
|
17
|
+
|
18
|
+
process :assigner, throttle: 10 do
|
14
19
|
UserAssigner.process!
|
15
20
|
end
|
16
21
|
|
17
|
-
process :sweeper, throttle:
|
22
|
+
process :sweeper, throttle: false, timeout: 20 do
|
18
23
|
Sweeper.sweep!
|
19
24
|
end
|
20
25
|
end
|
26
|
+
|
27
|
+
Robinhood.run
|
21
28
|
```
|
22
29
|
|
23
30
|
## How does it work?
|
24
31
|
|
25
32
|
Each time a process finishes its execution, the lock is released so any other
|
26
33
|
server (or system process) can execute it again. This ensures it will be
|
27
|
-
executed in a synchronous manner (one after the other).
|
34
|
+
executed in a synchronous manner (one after the other). It also garantees the
|
35
|
+
executions will be distributed across the processes (or servers) so if a server
|
36
|
+
goes down, the load will be distributed evenly across the rest of them.
|
28
37
|
|
29
38
|
You can also set a timeout (in case a process hangs for some reason) and a
|
30
39
|
throttling mechanism (so a process can't be re-scheduled before this time has
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "celluloid"
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module Robinhood
|
5
|
+
# The DSL provides syntax suger on top of Robinhood's runtime. It allows us
|
6
|
+
# to define processes in a natural language for the programmer's convenience.
|
7
|
+
#
|
8
|
+
class DSL
|
9
|
+
# Public: Initializes the DSL.
|
10
|
+
#
|
11
|
+
# runtime - A runtime that will be configured using the DSL.
|
12
|
+
def initialize(runtime)
|
13
|
+
@runtime = runtime
|
14
|
+
end
|
15
|
+
|
16
|
+
# Public: Adds a process to the runtime.
|
17
|
+
#
|
18
|
+
# name - The name of the process to be run. Will be used to identify the
|
19
|
+
# mutex name across System Processes.
|
20
|
+
# options - A set of options that will be passed to the Process.
|
21
|
+
# block - A block that will be called each execution of this process.
|
22
|
+
#
|
23
|
+
# Returns nil.
|
24
|
+
def process(name, options = {}, &block)
|
25
|
+
@runtime.add_process(name, options, block)
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
# Public: Allows a redis instance to be used to provide the locking.
|
30
|
+
#
|
31
|
+
# Example:
|
32
|
+
#
|
33
|
+
# Robinhood.define do
|
34
|
+
# redis{ Redis.new(host: 'foo') }
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# block - A mandatory block whose result will be assigned to the runtime's
|
38
|
+
# redis client.
|
39
|
+
#
|
40
|
+
# Returns nil.
|
41
|
+
def redis
|
42
|
+
@runtime.redis = yield
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/robinhood/process.rb
CHANGED
@@ -1,14 +1,36 @@
|
|
1
1
|
require 'celluloid'
|
2
|
-
require 'redis-mutex'
|
3
2
|
require 'benchmark'
|
3
|
+
require 'robinhood/mutex'
|
4
4
|
|
5
5
|
module Robinhood
|
6
|
+
# This class wraps a previously defined process. It's responsible to prevent
|
7
|
+
# the same process to be run at the sime time on different System Processes.
|
8
|
+
#
|
9
|
+
# A Process leverages a Celluloid Actor, so it will spawned in a different
|
10
|
+
# thread and communicate with the rest of the system with messages.
|
6
11
|
class Process
|
7
12
|
attr_reader :name, :options
|
8
13
|
|
9
14
|
include Celluloid
|
10
15
|
include Celluloid::Logger
|
11
16
|
|
17
|
+
finalizer :unlock
|
18
|
+
|
19
|
+
# Public: Initializes a Process
|
20
|
+
#
|
21
|
+
# name - A String that will be used as a global identifier. It's
|
22
|
+
# important that it is unique, since the locking will be
|
23
|
+
# performed this name as scope.
|
24
|
+
# options - A Hash of options that configure this Process.
|
25
|
+
# :throttle - An Integer that represents the minimum number of
|
26
|
+
# seconds that this process will take. In other
|
27
|
+
# words, if its execution takes less than that, we'll
|
28
|
+
# wait a little bit until it hits the next execution.
|
29
|
+
# :timeout - An Integer representing the period of time after
|
30
|
+
# which this process' execution will be considered as
|
31
|
+
# 'hung' and another execution will take place.
|
32
|
+
# block - The block that will be evaluated each time this Process gets
|
33
|
+
# executed.
|
12
34
|
def initialize(name, options, block)
|
13
35
|
@name = name
|
14
36
|
@options = options
|
@@ -16,17 +38,31 @@ module Robinhood
|
|
16
38
|
async.run
|
17
39
|
end
|
18
40
|
|
19
|
-
|
20
|
-
mutex.lock
|
41
|
+
private
|
21
42
|
|
22
|
-
|
43
|
+
def run
|
44
|
+
return unless lock
|
23
45
|
|
24
|
-
|
46
|
+
begin
|
47
|
+
time = Benchmark.realtime{ @block.call }
|
48
|
+
if difference = throttle - time
|
49
|
+
sleep(difference)
|
50
|
+
end
|
51
|
+
ensure
|
52
|
+
unlock
|
53
|
+
end
|
25
54
|
ensure
|
26
|
-
mutex.unlock
|
27
55
|
async.run
|
28
56
|
end
|
29
57
|
|
58
|
+
def lock
|
59
|
+
mutex.lock
|
60
|
+
end
|
61
|
+
|
62
|
+
def unlock
|
63
|
+
mutex.unlock
|
64
|
+
end
|
65
|
+
|
30
66
|
def lock_name
|
31
67
|
"robinhood:#{name}:lock"
|
32
68
|
end
|
@@ -35,12 +71,16 @@ module Robinhood
|
|
35
71
|
if (throttle = options[:throttle]) != nil
|
36
72
|
throttle
|
37
73
|
else
|
38
|
-
0.
|
74
|
+
0.05
|
39
75
|
end
|
40
76
|
end
|
41
77
|
|
78
|
+
def timeout
|
79
|
+
options[:timeout] || 300
|
80
|
+
end
|
81
|
+
|
42
82
|
def mutex
|
43
|
-
@mutex ||=
|
83
|
+
@mutex ||= Mutex.new(lock_name, block: 1, sleep: 0.1, expire: timeout)
|
44
84
|
end
|
45
85
|
end
|
46
86
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require "celluloid"
|
2
|
+
require "robinhood/process"
|
3
|
+
require 'robinhood/mutex'
|
4
|
+
|
5
|
+
module Robinhood
|
6
|
+
# A Runtime is responsible of kickstarting a Robinhood's execution, spawning
|
7
|
+
# all the processes and configuring the environment.
|
8
|
+
class Runtime
|
9
|
+
attr_accessor :redis
|
10
|
+
|
11
|
+
# Public: Initializes a Runtime.
|
12
|
+
def initialize
|
13
|
+
@processes = []
|
14
|
+
end
|
15
|
+
|
16
|
+
# Public: Schedules a process to be run in this Runtime.
|
17
|
+
#
|
18
|
+
# name - A String identifying this process.
|
19
|
+
# options - A Hash of options that will be passed to the underlying
|
20
|
+
# Process.
|
21
|
+
# block - The block that will be evaluated in this Process.
|
22
|
+
#
|
23
|
+
# Returns nil.
|
24
|
+
def add_process(name, options, block)
|
25
|
+
@processes << [name, options, block]
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
# Public: Starts the Runtime.
|
30
|
+
#
|
31
|
+
# options - A hash of options to configure this Runtime's execution.
|
32
|
+
# (default: {background: false})
|
33
|
+
# :background - True if it runs on the background (doesn't block
|
34
|
+
# the main thread), False otherwise.
|
35
|
+
#
|
36
|
+
# Returns the Runtime.
|
37
|
+
def run(options = {})
|
38
|
+
setup_supervision_group
|
39
|
+
Mutex.db = redis
|
40
|
+
|
41
|
+
Robinhood.log :info, "Starting Robin Hood: Robbing from the rich and giving to the poor.."
|
42
|
+
|
43
|
+
@actor = options[:background] ? supervision_group.run! : supervision_group.run
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
# Public: Stops this Runtime.
|
48
|
+
#
|
49
|
+
# Returns nil.
|
50
|
+
def stop
|
51
|
+
@actor.finalize if @actor
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def redis
|
58
|
+
@redis ||= Redis.new
|
59
|
+
end
|
60
|
+
|
61
|
+
def supervision_group
|
62
|
+
@supervision_group ||= Class.new(Celluloid::SupervisionGroup)
|
63
|
+
end
|
64
|
+
|
65
|
+
def setup_supervision_group
|
66
|
+
@processes.each do |process|
|
67
|
+
name, options, block = process
|
68
|
+
|
69
|
+
supervision_group.supervise Process,
|
70
|
+
as: "robinhood_#{name}",
|
71
|
+
args: [name, options, block]
|
72
|
+
end
|
73
|
+
|
74
|
+
@processes = []
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/robinhood/version.rb
CHANGED
data/lib/robinhood.rb
CHANGED
@@ -1,20 +1,89 @@
|
|
1
1
|
require "robinhood/version"
|
2
|
-
require "robinhood/
|
2
|
+
require "robinhood/dsl"
|
3
|
+
require "robinhood/runtime"
|
3
4
|
require "celluloid/autostart"
|
4
5
|
|
5
6
|
module Robinhood
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
# Public: The starting point for Robinhood's DSL.
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
#
|
11
|
+
# Robinhood.define do
|
12
|
+
# redis{ Redis.new(host: 'foobar') }
|
13
|
+
#
|
14
|
+
# process :ed, timeout: 100 do
|
15
|
+
# Balls.process!
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# process :sweeper, throttle: 5 do
|
19
|
+
# Sweeper.sweep!
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
def self.define(&block)
|
23
|
+
dsl.instance_eval(&block) if block
|
10
24
|
end
|
11
25
|
|
26
|
+
# Public: Runs a previously configured Robinhood instance.
|
27
|
+
#
|
28
|
+
# options - A hash of options to configure the execution.
|
29
|
+
# (default: {background: false})
|
30
|
+
# :background - True if it has to be run on the background (doesn't
|
31
|
+
# block the main thread), False otherwise.
|
32
|
+
#
|
33
|
+
# Returns nil.
|
34
|
+
def self.run(options = {})
|
35
|
+
runtime.run(options)
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
# Public: Stops Robinhood's execution, if it was run on the background.
|
40
|
+
#
|
41
|
+
# Returns nil.
|
12
42
|
def self.stop
|
13
|
-
|
43
|
+
runtime.stop if runtime
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
# Public: Assigns a Logger to Robinhood where it will output info, debug and
|
48
|
+
# error messages.
|
49
|
+
#
|
50
|
+
# Returns the Logger.
|
51
|
+
def self.logger=(logger)
|
52
|
+
@logger = logger
|
53
|
+
end
|
54
|
+
|
55
|
+
# Private: Logs messages to the logger.
|
56
|
+
#
|
57
|
+
# loglevel - The message's log level: :info, :error or :debug
|
58
|
+
# message - A String with the message to be logged
|
59
|
+
#
|
60
|
+
# Returns nil.
|
61
|
+
def self.log(loglevel, message)
|
62
|
+
logger.send loglevel, message if logger
|
63
|
+
nil
|
14
64
|
end
|
15
65
|
|
66
|
+
# Semi-public: Resets robinhood to a clean state. Mostly used on testing.
|
67
|
+
#
|
68
|
+
# Returns nil.
|
16
69
|
def self.reset!
|
17
70
|
stop
|
18
|
-
@
|
71
|
+
@runtime = nil
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.logger
|
75
|
+
return @logger if defined?(@logger)
|
76
|
+
@logger = Logger.new(STDOUT)
|
77
|
+
end
|
78
|
+
private_class_method :logger
|
79
|
+
|
80
|
+
def self.runtime
|
81
|
+
@runtime ||= Runtime.new
|
82
|
+
end
|
83
|
+
private_class_method :runtime
|
84
|
+
|
85
|
+
def self.dsl
|
86
|
+
@dsl ||= DSL.new(runtime)
|
19
87
|
end
|
88
|
+
private_class_method :dsl
|
20
89
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "robinhood"
|
3
|
+
|
4
|
+
describe "Robinhood.define" do
|
5
|
+
it "executes a process iteratively until it ends" do
|
6
|
+
queue = [1, 2]
|
7
|
+
|
8
|
+
Robinhood.define do
|
9
|
+
process :test do
|
10
|
+
queue.pop
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
Robinhood.run(background: true)
|
15
|
+
|
16
|
+
sleep(0.2)
|
17
|
+
expect(queue).to be_empty
|
18
|
+
end
|
19
|
+
|
20
|
+
it "allows setting an arbitrary throttle" do
|
21
|
+
throttle = 0.01
|
22
|
+
|
23
|
+
calls = []
|
24
|
+
|
25
|
+
method_call = Proc.new do
|
26
|
+
calls << Time.now
|
27
|
+
end
|
28
|
+
|
29
|
+
Robinhood.define do
|
30
|
+
process :test, throttle: throttle do
|
31
|
+
method_call.call
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Robinhood.run(background: true)
|
36
|
+
|
37
|
+
sleep(throttle * 5)
|
38
|
+
|
39
|
+
calls.each_with_index do |call, index|
|
40
|
+
if (index + 1) < calls.length
|
41
|
+
expect(calls[index + 1] - call).to be > throttle
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "robinhood"
|
3
|
+
require "robinhood/dsl"
|
4
|
+
|
5
|
+
module Robinhood
|
6
|
+
describe DSL do
|
7
|
+
let(:dsl){ DSL.new(runtime) }
|
8
|
+
|
9
|
+
describe "#process" do
|
10
|
+
let(:runtime){ double(:runtime, add_process: true) }
|
11
|
+
|
12
|
+
it "adds a process to the runtime" do
|
13
|
+
block = Proc.new{}
|
14
|
+
dsl.process :test, {foo: :bar}, &block
|
15
|
+
|
16
|
+
expect(runtime).to have_received(:add_process).
|
17
|
+
with(:test, {foo: :bar}, block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#redis" do
|
22
|
+
let(:runtime){ double(:runtime, :redis= => true) }
|
23
|
+
|
24
|
+
it "allows setting a redis client to the runtime" do
|
25
|
+
redis = double(:redis)
|
26
|
+
dsl.redis{ redis }
|
27
|
+
|
28
|
+
expect(runtime).to have_received(:redis=).with(redis)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "robinhood"
|
3
|
+
require "robinhood/process"
|
4
|
+
require 'thread'
|
5
|
+
|
6
|
+
module Robinhood
|
7
|
+
describe Process do
|
8
|
+
before do
|
9
|
+
Robinhood::Runtime.new.run(background: true)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "mutual exclusion" do
|
13
|
+
it "doesn't allow two of the same processes to run at once" do
|
14
|
+
output = []
|
15
|
+
|
16
|
+
Process.new(:test, {throttle: 0.01}, Proc.new{
|
17
|
+
output << :ed
|
18
|
+
sleep(0.05)
|
19
|
+
output << :ed
|
20
|
+
})
|
21
|
+
|
22
|
+
Process.new(:test, {throttle: 0.01}, Proc.new{
|
23
|
+
output << :balls
|
24
|
+
sleep(0.05)
|
25
|
+
output << :balls
|
26
|
+
})
|
27
|
+
|
28
|
+
sleep(2)
|
29
|
+
|
30
|
+
(output.length / 2).times.map{|i| i * 2}.each do |index|
|
31
|
+
expect(output[index]).to eq(output[index + 1])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "robinhood"
|
3
|
+
require "robinhood/runtime"
|
4
|
+
require 'thread'
|
5
|
+
|
6
|
+
module Robinhood
|
7
|
+
describe Runtime do
|
8
|
+
let(:runtime){ Robinhood::Runtime.new }
|
9
|
+
|
10
|
+
describe "#add_process" do
|
11
|
+
it "adds a process to be executed on start" do
|
12
|
+
runtime.add_process :edballs, {foo: :bar}, Proc.new{}
|
13
|
+
|
14
|
+
runtime.run(background: true)
|
15
|
+
|
16
|
+
actor = Celluloid::Actor[:robinhood_edballs]
|
17
|
+
expect(actor).to_not be_nil
|
18
|
+
expect(actor.options).to include(foo: :bar)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#run" do
|
23
|
+
it "sets the default redis db for the mutex" do
|
24
|
+
redis = double(:redis)
|
25
|
+
runtime.redis = redis
|
26
|
+
runtime.run(background: true)
|
27
|
+
|
28
|
+
expect(Mutex.db).to eq(redis)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#stop" do
|
33
|
+
it "stops the runtime synchronously" do
|
34
|
+
runtime.add_process :edballs, {foo: :bar}, Proc.new{}
|
35
|
+
|
36
|
+
runtime.run(background: true)
|
37
|
+
runtime.stop
|
38
|
+
|
39
|
+
expect(Celluloid::Actor[:robinhood_edballs]).to eq(nil)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -8,13 +8,13 @@ RSpec.configure do |config|
|
|
8
8
|
config.formatter = :documentation
|
9
9
|
end
|
10
10
|
|
11
|
-
config.before(:
|
11
|
+
config.before(:all) do
|
12
12
|
Celluloid.logger = nil
|
13
|
-
|
14
|
-
redis.flushdb
|
13
|
+
Robinhood.logger = nil
|
15
14
|
end
|
16
15
|
|
17
|
-
config.
|
16
|
+
config.before(:each) do
|
18
17
|
Robinhood.reset!
|
18
|
+
Redis.new.flushdb
|
19
19
|
end
|
20
20
|
end
|
metadata
CHANGED
@@ -1,128 +1,113 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: robinhood
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
version: 0.0.1.pre
|
4
|
+
version: 0.0.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Josep Jaume
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-10-
|
11
|
+
date: 2013-10-06 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
|
-
|
16
|
-
requirements:
|
17
|
-
- - ! '>='
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
none: false
|
14
|
+
name: celluloid
|
21
15
|
requirement: !ruby/object:Gem::Requirement
|
22
16
|
requirements:
|
23
|
-
- -
|
17
|
+
- - '>='
|
24
18
|
- !ruby/object:Gem::Version
|
25
19
|
version: '0'
|
26
|
-
none: false
|
27
|
-
name: celluloid
|
28
20
|
type: :runtime
|
29
21
|
prerelease: false
|
30
|
-
- !ruby/object:Gem::Dependency
|
31
22
|
version_requirements: !ruby/object:Gem::Requirement
|
32
23
|
requirements:
|
33
|
-
- -
|
24
|
+
- - '>='
|
34
25
|
- !ruby/object:Gem::Version
|
35
26
|
version: '0'
|
36
|
-
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: redis-mutex
|
37
29
|
requirement: !ruby/object:Gem::Requirement
|
38
30
|
requirements:
|
39
|
-
- -
|
31
|
+
- - '>='
|
40
32
|
- !ruby/object:Gem::Version
|
41
33
|
version: '0'
|
42
|
-
none: false
|
43
|
-
name: redis-mutex
|
44
34
|
type: :runtime
|
45
35
|
prerelease: false
|
46
|
-
- !ruby/object:Gem::Dependency
|
47
36
|
version_requirements: !ruby/object:Gem::Requirement
|
48
37
|
requirements:
|
49
|
-
- -
|
38
|
+
- - '>='
|
50
39
|
- !ruby/object:Gem::Version
|
51
|
-
version: '
|
52
|
-
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
53
43
|
requirement: !ruby/object:Gem::Requirement
|
54
44
|
requirements:
|
55
45
|
- - ~>
|
56
46
|
- !ruby/object:Gem::Version
|
57
47
|
version: '1.3'
|
58
|
-
none: false
|
59
|
-
name: bundler
|
60
48
|
type: :development
|
61
49
|
prerelease: false
|
62
|
-
- !ruby/object:Gem::Dependency
|
63
50
|
version_requirements: !ruby/object:Gem::Requirement
|
64
51
|
requirements:
|
65
|
-
- -
|
52
|
+
- - ~>
|
66
53
|
- !ruby/object:Gem::Version
|
67
|
-
version: '
|
68
|
-
|
54
|
+
version: '1.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
69
57
|
requirement: !ruby/object:Gem::Requirement
|
70
58
|
requirements:
|
71
|
-
- -
|
59
|
+
- - '>='
|
72
60
|
- !ruby/object:Gem::Version
|
73
61
|
version: '0'
|
74
|
-
none: false
|
75
|
-
name: rake
|
76
62
|
type: :development
|
77
63
|
prerelease: false
|
78
|
-
- !ruby/object:Gem::Dependency
|
79
64
|
version_requirements: !ruby/object:Gem::Requirement
|
80
65
|
requirements:
|
81
|
-
- -
|
66
|
+
- - '>='
|
82
67
|
- !ruby/object:Gem::Version
|
83
68
|
version: '0'
|
84
|
-
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
86
72
|
requirements:
|
87
|
-
- -
|
73
|
+
- - '>='
|
88
74
|
- !ruby/object:Gem::Version
|
89
75
|
version: '0'
|
90
|
-
none: false
|
91
|
-
name: rspec
|
92
76
|
type: :development
|
93
77
|
prerelease: false
|
94
|
-
- !ruby/object:Gem::Dependency
|
95
78
|
version_requirements: !ruby/object:Gem::Requirement
|
96
79
|
requirements:
|
97
|
-
- -
|
80
|
+
- - '>='
|
98
81
|
- !ruby/object:Gem::Version
|
99
82
|
version: '0'
|
100
|
-
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pry
|
101
85
|
requirement: !ruby/object:Gem::Requirement
|
102
86
|
requirements:
|
103
|
-
- -
|
87
|
+
- - '>='
|
104
88
|
- !ruby/object:Gem::Version
|
105
89
|
version: '0'
|
106
|
-
none: false
|
107
|
-
name: pry
|
108
90
|
type: :development
|
109
91
|
prerelease: false
|
110
|
-
- !ruby/object:Gem::Dependency
|
111
92
|
version_requirements: !ruby/object:Gem::Requirement
|
112
93
|
requirements:
|
113
|
-
- -
|
94
|
+
- - '>='
|
114
95
|
- !ruby/object:Gem::Version
|
115
96
|
version: '0'
|
116
|
-
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pry-nav
|
117
99
|
requirement: !ruby/object:Gem::Requirement
|
118
100
|
requirements:
|
119
|
-
- -
|
101
|
+
- - '>='
|
120
102
|
- !ruby/object:Gem::Version
|
121
103
|
version: '0'
|
122
|
-
none: false
|
123
|
-
name: pry-nav
|
124
104
|
type: :development
|
125
105
|
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
126
111
|
description: Robin hood leverages celluloid actors and redis-mutex to distribute long-lived,
|
127
112
|
single-instance processes across multiple servers.
|
128
113
|
email:
|
@@ -131,65 +116,53 @@ executables: []
|
|
131
116
|
extensions: []
|
132
117
|
extra_rdoc_files: []
|
133
118
|
files:
|
134
|
-
-
|
135
|
-
|
136
|
-
-
|
137
|
-
|
138
|
-
-
|
139
|
-
|
140
|
-
-
|
141
|
-
|
142
|
-
-
|
143
|
-
|
144
|
-
-
|
145
|
-
|
146
|
-
-
|
147
|
-
|
148
|
-
-
|
149
|
-
|
150
|
-
-
|
151
|
-
|
152
|
-
-
|
153
|
-
bGliL3JvYmluaG9vZC9zZXR1cC5yYg==
|
154
|
-
- !binary |-
|
155
|
-
bGliL3JvYmluaG9vZC92ZXJzaW9uLnJi
|
156
|
-
- !binary |-
|
157
|
-
cm9iaW5ob29kLmdlbXNwZWM=
|
158
|
-
- !binary |-
|
159
|
-
c3BlYy9hY2NlcHRhbmNlL3NldHVwX3NwZWMucmI=
|
160
|
-
- !binary |-
|
161
|
-
c3BlYy9zcGVjX2hlbHBlci5yYg==
|
119
|
+
- .gitignore
|
120
|
+
- .ruby-version
|
121
|
+
- .travis.yml
|
122
|
+
- Gemfile
|
123
|
+
- LICENSE.txt
|
124
|
+
- README.md
|
125
|
+
- Rakefile
|
126
|
+
- lib/robinhood.rb
|
127
|
+
- lib/robinhood/dsl.rb
|
128
|
+
- lib/robinhood/mutex.rb
|
129
|
+
- lib/robinhood/process.rb
|
130
|
+
- lib/robinhood/runtime.rb
|
131
|
+
- lib/robinhood/version.rb
|
132
|
+
- robinhood.gemspec
|
133
|
+
- spec/acceptance/dsl_spec.rb
|
134
|
+
- spec/lib/robinhood/dsl_spec.rb
|
135
|
+
- spec/lib/robinhood/process_spec.rb
|
136
|
+
- spec/lib/robinhood/runtime_spec.rb
|
137
|
+
- spec/spec_helper.rb
|
162
138
|
homepage: http://www.codegram.com
|
163
139
|
licenses:
|
164
140
|
- MIT
|
141
|
+
metadata: {}
|
165
142
|
post_install_message:
|
166
143
|
rdoc_options: []
|
167
144
|
require_paths:
|
168
145
|
- lib
|
169
146
|
required_ruby_version: !ruby/object:Gem::Requirement
|
170
147
|
requirements:
|
171
|
-
- -
|
148
|
+
- - '>='
|
172
149
|
- !ruby/object:Gem::Version
|
173
|
-
hash: 2002549777813010636
|
174
|
-
segments:
|
175
|
-
- 0
|
176
150
|
version: '0'
|
177
|
-
none: false
|
178
151
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
179
152
|
requirements:
|
180
|
-
- -
|
153
|
+
- - '>='
|
181
154
|
- !ruby/object:Gem::Version
|
182
|
-
version:
|
183
|
-
none: false
|
155
|
+
version: '0'
|
184
156
|
requirements: []
|
185
157
|
rubyforge_project:
|
186
|
-
rubygems_version:
|
158
|
+
rubygems_version: 2.0.3
|
187
159
|
signing_key:
|
188
|
-
specification_version:
|
160
|
+
specification_version: 4
|
189
161
|
summary: Robin hood leverages celluloid actors and redis-mutex to distribute long-lived,
|
190
162
|
single-instance processes across multiple servers.
|
191
163
|
test_files:
|
192
|
-
-
|
193
|
-
|
194
|
-
-
|
195
|
-
|
164
|
+
- spec/acceptance/dsl_spec.rb
|
165
|
+
- spec/lib/robinhood/dsl_spec.rb
|
166
|
+
- spec/lib/robinhood/process_spec.rb
|
167
|
+
- spec/lib/robinhood/runtime_spec.rb
|
168
|
+
- spec/spec_helper.rb
|
data/lib/robinhood/setup.rb
DELETED
@@ -1,54 +0,0 @@
|
|
1
|
-
require "robinhood/process"
|
2
|
-
require "celluloid"
|
3
|
-
|
4
|
-
module Robinhood
|
5
|
-
class Setup
|
6
|
-
attr_reader :options
|
7
|
-
|
8
|
-
def initialize(options = {})
|
9
|
-
@options = options
|
10
|
-
@processes = []
|
11
|
-
end
|
12
|
-
|
13
|
-
def process(name, options = {}, &block)
|
14
|
-
@processes << [name, options, block]
|
15
|
-
end
|
16
|
-
|
17
|
-
def redis
|
18
|
-
@redis = yield
|
19
|
-
end
|
20
|
-
|
21
|
-
def supervision_group
|
22
|
-
@supervision_group ||= Class.new(Celluloid::SupervisionGroup)
|
23
|
-
end
|
24
|
-
|
25
|
-
def start
|
26
|
-
setup_supervision_group
|
27
|
-
Redis::Classy.db = @redis || Redis.new
|
28
|
-
|
29
|
-
@supervision_group_actor = if options[:background]
|
30
|
-
supervision_group.run!
|
31
|
-
else
|
32
|
-
supervision_group.run
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def stop
|
37
|
-
@supervision_group_actor.finalize if @supervision_group_actor
|
38
|
-
end
|
39
|
-
|
40
|
-
def redis_options
|
41
|
-
options[:redis] || {}
|
42
|
-
end
|
43
|
-
|
44
|
-
def setup_supervision_group
|
45
|
-
@processes.each do |process|
|
46
|
-
name, options, block = process
|
47
|
-
|
48
|
-
supervision_group.supervise Process,
|
49
|
-
as: "robinhood_#{name}",
|
50
|
-
args: [name, options.merge(autostart: true), block]
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
require "robinhood"
|
3
|
-
|
4
|
-
module Robinhood
|
5
|
-
describe ".setup" do
|
6
|
-
it "creates actors for each process" do
|
7
|
-
Robinhood.setup(background: true) do
|
8
|
-
process(:ed){}
|
9
|
-
process(:balls){}
|
10
|
-
end
|
11
|
-
|
12
|
-
sleep(1)
|
13
|
-
|
14
|
-
expect(Celluloid::Actor[:robinhood_ed]).to_not be_nil
|
15
|
-
expect(Celluloid::Actor[:robinhood_balls]).to_not be_nil
|
16
|
-
end
|
17
|
-
|
18
|
-
it "executes a process iteratively until it ends" do
|
19
|
-
queue = [1, 2]
|
20
|
-
|
21
|
-
Robinhood.setup(background: true) do
|
22
|
-
process :test do
|
23
|
-
queue.pop
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
sleep(1)
|
28
|
-
expect(queue).to be_empty
|
29
|
-
end
|
30
|
-
|
31
|
-
it "allows setting an arbitrary throttle" do
|
32
|
-
queue = [1, 2]
|
33
|
-
|
34
|
-
Robinhood.setup(background: true) do
|
35
|
-
process :test, throttle: 0.5 do
|
36
|
-
queue.pop
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
sleep(0.2)
|
41
|
-
expect(queue).not_to be_empty
|
42
|
-
sleep(1)
|
43
|
-
expect(queue).to be_empty
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|