dcell 0.9.0 → 0.10.0
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.
- 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
|