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.
Files changed (64) hide show
  1. data/.travis.yml +4 -1
  2. data/CHANGES.md +11 -0
  3. data/Gemfile +6 -3
  4. data/LICENSE.txt +1 -1
  5. data/README.md +27 -243
  6. data/benchmarks/receiver.rb +2 -2
  7. data/dcell.gemspec +7 -7
  8. data/explorer/css/bootstrap-responsive.css +686 -0
  9. data/explorer/css/bootstrap-responsive.min.css +12 -0
  10. data/explorer/css/bootstrap.css +3990 -0
  11. data/explorer/css/bootstrap.min.css +689 -0
  12. data/explorer/css/explorer.css +28 -0
  13. data/explorer/ico/favicon.ico +0 -0
  14. data/explorer/img/glyphicons-halflings-white.png +0 -0
  15. data/explorer/img/glyphicons-halflings.png +0 -0
  16. data/explorer/img/logo.png +0 -0
  17. data/explorer/index.html.erb +94 -0
  18. data/explorer/js/bootstrap.js +1726 -0
  19. data/explorer/js/bootstrap.min.js +6 -0
  20. data/lib/dcell.rb +27 -2
  21. data/lib/dcell/celluloid_ext.rb +14 -3
  22. data/lib/dcell/directory.rb +15 -3
  23. data/lib/dcell/explorer.rb +76 -0
  24. data/lib/dcell/future_proxy.rb +32 -0
  25. data/lib/dcell/info_service.rb +117 -0
  26. data/lib/dcell/mailbox_proxy.rb +6 -7
  27. data/lib/dcell/messages.rb +5 -6
  28. data/lib/dcell/node.rb +25 -55
  29. data/lib/dcell/node_manager.rb +81 -0
  30. data/lib/dcell/registries/cassandra_adapter.rb +86 -0
  31. data/lib/dcell/registries/gossip/core.rb +235 -0
  32. data/lib/dcell/registries/gossip_adapter.rb +26 -0
  33. data/lib/dcell/registries/moneta_adapter.rb +0 -7
  34. data/lib/dcell/registries/redis_adapter.rb +0 -31
  35. data/lib/dcell/registries/zk_adapter.rb +1 -39
  36. data/lib/dcell/router.rb +37 -30
  37. data/lib/dcell/rpc.rb +23 -23
  38. data/lib/dcell/server.rb +5 -2
  39. data/lib/dcell/version.rb +1 -1
  40. data/logo.png +0 -0
  41. data/spec/dcell/actor_proxy_spec.rb +4 -0
  42. data/spec/dcell/celluloid_ext_spec.rb +11 -0
  43. data/spec/dcell/directory_spec.rb +1 -1
  44. data/spec/dcell/explorer_spec.rb +17 -0
  45. data/spec/dcell/global_spec.rb +4 -0
  46. data/spec/dcell/registries/gossip_adapter_spec.rb +6 -0
  47. data/spec/spec_helper.rb +14 -7
  48. data/spec/support/registry_examples.rb +0 -18
  49. data/tasks/cassandra.task +84 -0
  50. metadata +55 -35
  51. data/celluloid-zmq/.gitignore +0 -17
  52. data/celluloid-zmq/.rspec +0 -4
  53. data/celluloid-zmq/CHANGES.md +0 -31
  54. data/celluloid-zmq/Gemfile +0 -7
  55. data/celluloid-zmq/README.md +0 -56
  56. data/celluloid-zmq/Rakefile +0 -7
  57. data/celluloid-zmq/celluloid-zmq.gemspec +0 -28
  58. data/celluloid-zmq/lib/celluloid/zmq.rb +0 -36
  59. data/celluloid-zmq/lib/celluloid/zmq/reactor.rb +0 -90
  60. data/celluloid-zmq/lib/celluloid/zmq/sockets.rb +0 -130
  61. data/celluloid-zmq/lib/celluloid/zmq/version.rb +0 -5
  62. data/celluloid-zmq/lib/celluloid/zmq/waker.rb +0 -55
  63. data/celluloid-zmq/spec/celluloid/zmq/actor_spec.rb +0 -6
  64. 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
@@ -3,66 +3,73 @@ require 'weakref'
3
3
  module DCell
4
4
  # Route incoming messages to their recipient actors
5
5
  class Router
6
- @lock = Mutex.new
7
- @table = {}
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
- id = mailbox.object_id.to_s(16)
13
-
14
- @lock.synchronize do
15
- ref = @table[id]
16
- unless ref && ref.weakref_alive?
17
- @table[id] = WeakRef.new(mailbox)
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
- id
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 ID
25
- def find(mailbox_id)
26
- @lock.synchronize do
27
- ref = @table[mailbox_id]
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
- begin
30
- ref.__getobj__
31
- rescue WeakRef::RefError
32
- # The referenced actor is dead, so prune the registry
33
- @table.delete mailbox_id
34
- nil
35
- end
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(mailbox_id, message)
41
- recipient = find mailbox_id
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: #{mailbox_id.inspect}")
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(mailbox_id, event)
52
- recipient = find mailbox_id
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: #{mailbox_id.inspect}")
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
- @lock.synchronize do
64
- @table.each do |id, ref|
65
- @table.delete id unless ref.weakref_alive?
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
@@ -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(/^([0-9a-fA-F]+)@(.+?):$/)
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
- call_id, node_id = match[1], match[2]
21
+ uuid, node_id = match[1], match[2]
24
22
 
25
23
  if DCell.id == node_id
26
- Manager.claim Integer("0x#{call_id}")
24
+ Manager.claim uuid
27
25
  else
28
26
  caller, method, arguments, block = Marshal.load(string)
29
- RPC.new("#{call_id}@#{node_id}", caller, method, arguments, block)
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
- call_id = @ids[call.object_id]
43
- unless call_id
44
- call_id = @serial
45
- @serial += 1
46
- @ids[call.object_id] = call_id
47
- end
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
- @calls[call_id] = WeakRef.new(call)
50
- call_id
51
- ensure
52
- @mutex.unlock
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
- ref = @calls.delete(call_id)
58
- ref.__getobj__ if ref
59
- rescue WeakRef::RefError
60
- # Nothing to see here, folks
61
- ensure
62
- @mutex.unlock
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
@@ -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 Exception => ex
45
+ rescue => ex
43
46
  Celluloid::Logger.crash("DCell::Server: message dispatch failed", ex)
44
47
  end
45
48
  end
@@ -1,3 +1,3 @@
1
1
  module DCell
2
- VERSION = "0.9.0"
2
+ VERSION = "0.10.0"
3
3
  end
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
@@ -5,4 +5,4 @@ describe DCell::Directory do
5
5
  DCell::Directory["foobar"] = "tcp://localhost:1870"
6
6
  DCell::Directory["foobar"].should == "tcp://localhost:1870"
7
7
  end
8
- end
8
+ 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
@@ -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
 
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+
3
+ describe DCell::Registry::GossipAdapter do
4
+ subject { DCell::Registry::GossipAdapter.new :env => "test" }
5
+ it_behaves_like "a DCell registry"
6
+ end
@@ -5,12 +5,19 @@ Bundler.setup
5
5
  require 'dcell'
6
6
  Dir['./spec/support/*.rb'].map { |f| require f }
7
7
 
8
- DCell.setup
9
- DCell.run!
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
- at_exit do
15
- TestNode.stop
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