dcell 0.9.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +4 -1
- data/CHANGES.md +11 -0
- data/Gemfile +6 -3
- data/LICENSE.txt +1 -1
- data/README.md +27 -243
- data/benchmarks/receiver.rb +2 -2
- data/dcell.gemspec +7 -7
- data/explorer/css/bootstrap-responsive.css +686 -0
- data/explorer/css/bootstrap-responsive.min.css +12 -0
- data/explorer/css/bootstrap.css +3990 -0
- data/explorer/css/bootstrap.min.css +689 -0
- data/explorer/css/explorer.css +28 -0
- data/explorer/ico/favicon.ico +0 -0
- data/explorer/img/glyphicons-halflings-white.png +0 -0
- data/explorer/img/glyphicons-halflings.png +0 -0
- data/explorer/img/logo.png +0 -0
- data/explorer/index.html.erb +94 -0
- data/explorer/js/bootstrap.js +1726 -0
- data/explorer/js/bootstrap.min.js +6 -0
- data/lib/dcell.rb +27 -2
- data/lib/dcell/celluloid_ext.rb +14 -3
- data/lib/dcell/directory.rb +15 -3
- data/lib/dcell/explorer.rb +76 -0
- data/lib/dcell/future_proxy.rb +32 -0
- data/lib/dcell/info_service.rb +117 -0
- data/lib/dcell/mailbox_proxy.rb +6 -7
- data/lib/dcell/messages.rb +5 -6
- data/lib/dcell/node.rb +25 -55
- data/lib/dcell/node_manager.rb +81 -0
- data/lib/dcell/registries/cassandra_adapter.rb +86 -0
- data/lib/dcell/registries/gossip/core.rb +235 -0
- data/lib/dcell/registries/gossip_adapter.rb +26 -0
- data/lib/dcell/registries/moneta_adapter.rb +0 -7
- data/lib/dcell/registries/redis_adapter.rb +0 -31
- data/lib/dcell/registries/zk_adapter.rb +1 -39
- data/lib/dcell/router.rb +37 -30
- data/lib/dcell/rpc.rb +23 -23
- data/lib/dcell/server.rb +5 -2
- data/lib/dcell/version.rb +1 -1
- data/logo.png +0 -0
- data/spec/dcell/actor_proxy_spec.rb +4 -0
- data/spec/dcell/celluloid_ext_spec.rb +11 -0
- data/spec/dcell/directory_spec.rb +1 -1
- data/spec/dcell/explorer_spec.rb +17 -0
- data/spec/dcell/global_spec.rb +4 -0
- data/spec/dcell/registries/gossip_adapter_spec.rb +6 -0
- data/spec/spec_helper.rb +14 -7
- data/spec/support/registry_examples.rb +0 -18
- data/tasks/cassandra.task +84 -0
- metadata +55 -35
- data/celluloid-zmq/.gitignore +0 -17
- data/celluloid-zmq/.rspec +0 -4
- data/celluloid-zmq/CHANGES.md +0 -31
- data/celluloid-zmq/Gemfile +0 -7
- data/celluloid-zmq/README.md +0 -56
- data/celluloid-zmq/Rakefile +0 -7
- data/celluloid-zmq/celluloid-zmq.gemspec +0 -28
- data/celluloid-zmq/lib/celluloid/zmq.rb +0 -36
- data/celluloid-zmq/lib/celluloid/zmq/reactor.rb +0 -90
- data/celluloid-zmq/lib/celluloid/zmq/sockets.rb +0 -130
- data/celluloid-zmq/lib/celluloid/zmq/version.rb +0 -5
- data/celluloid-zmq/lib/celluloid/zmq/waker.rb +0 -55
- data/celluloid-zmq/spec/celluloid/zmq/actor_spec.rb +0 -6
- data/celluloid-zmq/spec/spec_helper.rb +0 -2
@@ -14,44 +14,13 @@ module DCell
|
|
14
14
|
redis = Redis.new options
|
15
15
|
@redis = Redis::Namespace.new @namespace, :redis => redis
|
16
16
|
|
17
|
-
@node_registry = NodeRegistry.new(@redis)
|
18
17
|
@global_registry = GlobalRegistry.new(@redis)
|
19
18
|
end
|
20
19
|
|
21
|
-
def clear_nodes
|
22
|
-
@node_registry.clear
|
23
|
-
end
|
24
|
-
|
25
20
|
def clear_globals
|
26
21
|
@global_registry.clear
|
27
22
|
end
|
28
23
|
|
29
|
-
class NodeRegistry
|
30
|
-
def initialize(redis)
|
31
|
-
@redis = redis
|
32
|
-
end
|
33
|
-
|
34
|
-
def get(node_id)
|
35
|
-
@redis.hget 'nodes', node_id
|
36
|
-
end
|
37
|
-
|
38
|
-
def set(node_id, addr)
|
39
|
-
@redis.hset 'nodes', node_id, addr
|
40
|
-
end
|
41
|
-
|
42
|
-
def nodes
|
43
|
-
@redis.hkeys 'nodes'
|
44
|
-
end
|
45
|
-
|
46
|
-
def clear
|
47
|
-
@redis.del 'nodes'
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def get_node(node_id); @node_registry.get(node_id) end
|
52
|
-
def set_node(node_id, addr); @node_registry.set(node_id, addr) end
|
53
|
-
def nodes; @node_registry.nodes end
|
54
|
-
|
55
24
|
class GlobalRegistry
|
56
25
|
def initialize(redis)
|
57
26
|
@redis = redis
|
@@ -36,51 +36,13 @@ module DCell
|
|
36
36
|
end
|
37
37
|
|
38
38
|
@zk = ZK.new(*servers)
|
39
|
-
@node_registry = NodeRegistry.new(@zk, @base_path)
|
40
39
|
@global_registry = GlobalRegistry.new(@zk, @base_path)
|
41
40
|
end
|
42
41
|
|
43
|
-
def clear_nodes
|
44
|
-
@node_registry.clear
|
45
|
-
end
|
46
|
-
|
47
42
|
def clear_globals
|
48
43
|
@global_registry.clear
|
49
44
|
end
|
50
45
|
|
51
|
-
class NodeRegistry
|
52
|
-
def initialize(zk, base_path)
|
53
|
-
@zk, @base_path = zk, "#{base_path}/nodes"
|
54
|
-
@zk.mkdir_p @base_path
|
55
|
-
end
|
56
|
-
|
57
|
-
def get(node_id)
|
58
|
-
result, _ = @zk.get("#{@base_path}/#{node_id}")
|
59
|
-
result
|
60
|
-
rescue ZK::Exceptions::NoNode
|
61
|
-
end
|
62
|
-
|
63
|
-
def set(node_id, addr)
|
64
|
-
path = "#{@base_path}/#{node_id}"
|
65
|
-
@zk.set path, addr
|
66
|
-
rescue ZK::Exceptions::NoNode
|
67
|
-
@zk.create path, addr
|
68
|
-
end
|
69
|
-
|
70
|
-
def nodes
|
71
|
-
@zk.children @base_path
|
72
|
-
end
|
73
|
-
|
74
|
-
def clear
|
75
|
-
@zk.rm_rf @base_path
|
76
|
-
@zk.mkdir_p @base_path
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def get_node(node_id); @node_registry.get(node_id) end
|
81
|
-
def set_node(node_id, addr); @node_registry.set(node_id, addr) end
|
82
|
-
def nodes; @node_registry.nodes end
|
83
|
-
|
84
46
|
class GlobalRegistry
|
85
47
|
def initialize(zk, base_path)
|
86
48
|
@zk, @base_path = zk, "#{base_path}/globals"
|
@@ -119,4 +81,4 @@ module DCell
|
|
119
81
|
def global_keys; @global_registry.global_keys end
|
120
82
|
end
|
121
83
|
end
|
122
|
-
end
|
84
|
+
end
|
data/lib/dcell/router.rb
CHANGED
@@ -3,66 +3,73 @@ require 'weakref'
|
|
3
3
|
module DCell
|
4
4
|
# Route incoming messages to their recipient actors
|
5
5
|
class Router
|
6
|
-
@
|
7
|
-
@
|
6
|
+
@mutex = Mutex.new
|
7
|
+
@addresses = {}
|
8
|
+
@mailboxes = {}
|
8
9
|
|
9
10
|
class << self
|
10
11
|
# Enter a mailbox into the registry
|
11
12
|
def register(mailbox)
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
@
|
13
|
+
@mutex.lock
|
14
|
+
begin
|
15
|
+
address = @addresses[mailbox.object_id]
|
16
|
+
unless address
|
17
|
+
address = Celluloid.uuid
|
18
|
+
@addresses[mailbox.object_id] = address
|
18
19
|
end
|
19
|
-
end
|
20
20
|
|
21
|
-
|
21
|
+
ref = @mailboxes[address]
|
22
|
+
@mailboxes[address] = WeakRef.new(mailbox) unless ref && ref.weakref_alive?
|
23
|
+
|
24
|
+
address
|
25
|
+
ensure
|
26
|
+
@mutex.unlock rescue nil
|
27
|
+
end
|
22
28
|
end
|
23
29
|
|
24
|
-
# Find a mailbox by its
|
25
|
-
def find(
|
26
|
-
@lock
|
27
|
-
|
30
|
+
# Find a mailbox by its address
|
31
|
+
def find(mailbox_address)
|
32
|
+
@mutex.lock
|
33
|
+
begin
|
34
|
+
ref = @mailboxes[mailbox_address]
|
28
35
|
return unless ref
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
+
ref.__getobj__
|
37
|
+
rescue WeakRef::RefError
|
38
|
+
# The referenced actor is dead, so prune the registry
|
39
|
+
@mailboxes.delete mailbox_address
|
40
|
+
nil
|
41
|
+
ensure
|
42
|
+
@mutex.unlock rescue nil
|
36
43
|
end
|
37
44
|
end
|
38
45
|
|
39
46
|
# Route a message to a given mailbox ID
|
40
|
-
def route(
|
41
|
-
recipient = find
|
47
|
+
def route(recipient, message)
|
48
|
+
recipient = find(recipient) if recipient.is_a? String
|
42
49
|
|
43
50
|
if recipient
|
44
51
|
recipient << message
|
45
52
|
else
|
46
|
-
Celluloid::Logger.debug("received message for invalid actor: #{
|
53
|
+
Celluloid::Logger.debug("received message for invalid actor: #{mailbox_address.inspect}")
|
47
54
|
end
|
48
55
|
end
|
49
56
|
|
50
57
|
# Route a system event to a given mailbox ID
|
51
|
-
def route_system_event(
|
52
|
-
recipient = find
|
58
|
+
def route_system_event(mailbox_address, event)
|
59
|
+
recipient = find mailbox_address
|
53
60
|
|
54
61
|
if recipient
|
55
62
|
recipient.system_event event
|
56
63
|
else
|
57
|
-
Celluloid::Logger.debug("received message for invalid actor: #{
|
64
|
+
Celluloid::Logger.debug("received message for invalid actor: #{mailbox_address.inspect}")
|
58
65
|
end
|
59
66
|
end
|
60
67
|
|
61
68
|
# Prune all entries that point to dead objects
|
62
69
|
def gc
|
63
|
-
@
|
64
|
-
@
|
65
|
-
@
|
70
|
+
@mutex.synchronize do
|
71
|
+
@mailboxes.each do |id, ref|
|
72
|
+
@mailboxes.delete id unless ref.weakref_alive?
|
66
73
|
end
|
67
74
|
end
|
68
75
|
end
|
data/lib/dcell/rpc.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
require 'weakref'
|
2
2
|
|
3
3
|
module DCell
|
4
|
-
EPOCH = Time.gm(2012) # All things begin in 2012
|
5
|
-
|
6
4
|
class RPC < Celluloid::SyncCall
|
7
5
|
def initialize(id, caller, method, arguments, block)
|
8
6
|
@id, @caller, @method, @arguments, @block = id, caller, method, arguments, block
|
@@ -17,16 +15,16 @@ module DCell
|
|
17
15
|
# Loader for custom marshal format
|
18
16
|
def self._load(string)
|
19
17
|
id = string.slice!(0, string.index(":") + 1)
|
20
|
-
match = id.match(/^([
|
18
|
+
match = id.match(/^([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})@(.+?):$/)
|
21
19
|
raise ArgumentError, "couldn't parse call ID" unless match
|
22
20
|
|
23
|
-
|
21
|
+
uuid, node_id = match[1], match[2]
|
24
22
|
|
25
23
|
if DCell.id == node_id
|
26
|
-
Manager.claim
|
24
|
+
Manager.claim uuid
|
27
25
|
else
|
28
26
|
caller, method, arguments, block = Marshal.load(string)
|
29
|
-
RPC.new("#{
|
27
|
+
RPC.new("#{uuid}@#{node_id}", caller, method, arguments, block)
|
30
28
|
end
|
31
29
|
end
|
32
30
|
|
@@ -35,31 +33,33 @@ module DCell
|
|
35
33
|
@mutex = Mutex.new
|
36
34
|
@ids = {}
|
37
35
|
@calls = {}
|
38
|
-
@serial = Integer(Time.now - EPOCH) * 0x100000
|
39
36
|
|
40
37
|
def self.register(call)
|
41
38
|
@mutex.lock
|
42
|
-
|
43
|
-
|
44
|
-
call_id
|
45
|
-
|
46
|
-
|
47
|
-
|
39
|
+
begin
|
40
|
+
call_id = @ids[call.object_id]
|
41
|
+
unless call_id
|
42
|
+
call_id = Celluloid.uuid
|
43
|
+
@ids[call.object_id] = call_id
|
44
|
+
end
|
48
45
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
46
|
+
@calls[call_id] = WeakRef.new(call)
|
47
|
+
call_id
|
48
|
+
ensure
|
49
|
+
@mutex.unlock rescue nil
|
50
|
+
end
|
53
51
|
end
|
54
52
|
|
55
53
|
def self.claim(call_id)
|
56
54
|
@mutex.lock
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
55
|
+
begin
|
56
|
+
ref = @calls.delete(call_id)
|
57
|
+
ref.__getobj__ if ref
|
58
|
+
rescue WeakRef::RefError
|
59
|
+
# Nothing to see here, folks
|
60
|
+
ensure
|
61
|
+
@mutex.unlock rescue nil
|
62
|
+
end
|
63
63
|
end
|
64
64
|
end
|
65
65
|
end
|
data/lib/dcell/server.rb
CHANGED
@@ -5,6 +5,9 @@ module DCell
|
|
5
5
|
|
6
6
|
# Bind to the given 0MQ address (in URL form ala tcp://host:port)
|
7
7
|
def initialize
|
8
|
+
# The gossip protocol is dependent on the node manager
|
9
|
+
link Actor[:node_manager]
|
10
|
+
|
8
11
|
@addr = DCell.addr
|
9
12
|
@socket = PullSocket.new
|
10
13
|
|
@@ -20,7 +23,7 @@ module DCell
|
|
20
23
|
|
21
24
|
# Wait for incoming 0MQ messages
|
22
25
|
def run
|
23
|
-
while true; handle_message @socket.read; end
|
26
|
+
while true; handle_message! @socket.read; end
|
24
27
|
end
|
25
28
|
|
26
29
|
# Shut down the server
|
@@ -39,7 +42,7 @@ module DCell
|
|
39
42
|
|
40
43
|
begin
|
41
44
|
message.dispatch
|
42
|
-
rescue
|
45
|
+
rescue => ex
|
43
46
|
Celluloid::Logger.crash("DCell::Server: message dispatch failed", ex)
|
44
47
|
end
|
45
48
|
end
|
data/lib/dcell/version.rb
CHANGED
data/logo.png
CHANGED
Binary file
|
@@ -26,6 +26,10 @@ describe DCell::ActorProxy do
|
|
26
26
|
@remote_actor.value.should == 42
|
27
27
|
end
|
28
28
|
|
29
|
+
it "makes future calls to remote actors" do
|
30
|
+
@remote_actor.future(:value).value.should == 42
|
31
|
+
end
|
32
|
+
|
29
33
|
context :linking do
|
30
34
|
before :each do
|
31
35
|
@local_actor = LocalActor.new
|
@@ -4,6 +4,10 @@ describe Celluloid, "extensions" do
|
|
4
4
|
before do
|
5
5
|
class WillKane
|
6
6
|
include Celluloid
|
7
|
+
|
8
|
+
def speak
|
9
|
+
"Don't shove me Harv."
|
10
|
+
end
|
7
11
|
end
|
8
12
|
@marshal = WillKane.new
|
9
13
|
end
|
@@ -18,4 +22,11 @@ describe Celluloid, "extensions" do
|
|
18
22
|
string = Marshal.dump(@marshal.mailbox)
|
19
23
|
Marshal.load(string).should be_alive
|
20
24
|
end
|
25
|
+
|
26
|
+
it "marshals Celluloid::Future objects" do
|
27
|
+
future = @marshal.future(:speak)
|
28
|
+
future.should be_a(Celluloid::Future)
|
29
|
+
string = Marshal.dump(future)
|
30
|
+
Marshal.load(string).value.should == "Don't shove me Harv."
|
31
|
+
end
|
21
32
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dcell/explorer'
|
3
|
+
|
4
|
+
describe DCell::Explorer do
|
5
|
+
EXPLORER_HOST = 'localhost'
|
6
|
+
EXPLORER_PORT = 7778
|
7
|
+
EXPLORER_BASE = "http://#{EXPLORER_HOST}:#{EXPLORER_PORT}"
|
8
|
+
|
9
|
+
before do
|
10
|
+
@explorer = DCell::Explorer.new(EXPLORER_HOST, EXPLORER_PORT)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "reports the current node's status" do
|
14
|
+
response = Net::HTTP.get URI(EXPLORER_BASE)
|
15
|
+
response[%r{<h1><.*?> ([\w\.\-]+)<\/h1>}, 1].should == DCell.id
|
16
|
+
end
|
17
|
+
end
|
data/spec/dcell/global_spec.rb
CHANGED
@@ -11,6 +11,10 @@ describe DCell::Global do
|
|
11
11
|
|
12
12
|
# Double check the global value is available on all nodes
|
13
13
|
node = DCell::Node['test_node']
|
14
|
+
20.downto(0) do |i|
|
15
|
+
break if node[:test_actor].the_answer
|
16
|
+
sleep 1
|
17
|
+
end
|
14
18
|
node[:test_actor].the_answer.should == 42
|
15
19
|
end
|
16
20
|
|
data/spec/spec_helper.rb
CHANGED
@@ -5,12 +5,19 @@ Bundler.setup
|
|
5
5
|
require 'dcell'
|
6
6
|
Dir['./spec/support/*.rb'].map { |f| require f }
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
RSpec.configure do |config|
|
9
|
+
config.before(:suite) do
|
10
|
+
DCell.setup :directory => { :id => 'test_node', :addr => "tcp://127.0.0.1:#{TestNode::PORT}" }
|
11
|
+
@supervisor = DCell.run!
|
10
12
|
|
11
|
-
TestNode.start
|
12
|
-
TestNode.wait_until_ready
|
13
|
+
TestNode.start
|
14
|
+
TestNode.wait_until_ready
|
15
|
+
end
|
13
16
|
|
14
|
-
|
15
|
-
|
16
|
-
end
|
17
|
+
config.after(:suite) do
|
18
|
+
TestNode.stop
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# FIXME: this is hax to bypass the other at_exit handlers
|
23
|
+
at_exit { exit! $!.status }
|
@@ -1,22 +1,4 @@
|
|
1
1
|
shared_context "a DCell registry" do
|
2
|
-
context "node registry" do
|
3
|
-
before :each do
|
4
|
-
subject.clear_nodes
|
5
|
-
end
|
6
|
-
|
7
|
-
it "stores node addresses" do
|
8
|
-
address = "tcp://localhost:7777"
|
9
|
-
|
10
|
-
subject.set_node("foobar", address)
|
11
|
-
subject.get_node("foobar").should == address
|
12
|
-
end
|
13
|
-
|
14
|
-
it "stores the IDs of all nodes" do
|
15
|
-
subject.set_node("foobar", "tcp://localhost:7777")
|
16
|
-
subject.nodes.should include "foobar"
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
2
|
context "global registry" do
|
21
3
|
before :each do
|
22
4
|
subject.clear_globals
|