stn-dcell 0.16.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.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +7 -0
- data/.rspec +4 -0
- data/.travis.yml +30 -0
- data/CHANGES.md +53 -0
- data/Gemfile +18 -0
- data/LICENSE.txt +20 -0
- data/README.md +168 -0
- data/Rakefile +4 -0
- data/benchmarks/messaging.rb +73 -0
- data/benchmarks/receiver.rb +37 -0
- data/dcell.gemspec +29 -0
- data/examples/itchy.rb +26 -0
- data/examples/scratchy.rb +12 -0
- 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 +127 -0
- data/lib/dcell/actor_proxy.rb +30 -0
- data/lib/dcell/celluloid_ext.rb +120 -0
- data/lib/dcell/directory.rb +31 -0
- data/lib/dcell/explorer.rb +74 -0
- data/lib/dcell/future_proxy.rb +32 -0
- data/lib/dcell/global.rb +23 -0
- data/lib/dcell/info_service.rb +122 -0
- data/lib/dcell/mailbox_proxy.rb +53 -0
- data/lib/dcell/messages.rb +65 -0
- data/lib/dcell/node.rb +138 -0
- data/lib/dcell/node_manager.rb +79 -0
- data/lib/dcell/registries/cassandra_adapter.rb +126 -0
- data/lib/dcell/registries/mongodb_adapter.rb +85 -0
- data/lib/dcell/registries/redis_adapter.rb +95 -0
- data/lib/dcell/registries/zk_adapter.rb +123 -0
- data/lib/dcell/responses.rb +16 -0
- data/lib/dcell/router.rb +46 -0
- data/lib/dcell/rpc.rb +95 -0
- data/lib/dcell/rspec.rb +1 -0
- data/lib/dcell/server.rb +73 -0
- data/lib/dcell/version.rb +3 -0
- data/logo.png +0 -0
- data/spec/dcell/actor_proxy_spec.rb +72 -0
- data/spec/dcell/celluloid_ext_spec.rb +32 -0
- data/spec/dcell/directory_spec.rb +22 -0
- data/spec/dcell/explorer_spec.rb +17 -0
- data/spec/dcell/global_spec.rb +25 -0
- data/spec/dcell/node_spec.rb +23 -0
- data/spec/dcell/registries/mongodb_adapter_spec.rb +7 -0
- data/spec/dcell/registries/redis_adapter_spec.rb +6 -0
- data/spec/dcell/registries/zk_adapter_spec.rb +28 -0
- data/spec/options/01-options.rb +10 -0
- data/spec/options/02-registry.rb +13 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/support/helpers.rb +46 -0
- data/spec/support/registry_examples.rb +35 -0
- data/spec/test_node.rb +38 -0
- data/tasks/cassandra.task +84 -0
- data/tasks/rspec.task +7 -0
- data/tasks/zookeeper.task +58 -0
- metadata +239 -0
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
3
|
+
module DCell
|
4
|
+
class InfoService
|
5
|
+
include Celluloid
|
6
|
+
attr_reader :os, :os_version, :hostname, :platform, :distribution
|
7
|
+
attr_reader :cpu_arch, :cpu_type, :cpu_vendor, :cpu_speed, :cpu_count
|
8
|
+
attr_reader :ruby_version, :ruby_engine, :ruby_platform
|
9
|
+
|
10
|
+
UPTIME_REGEX = /up ((\d+ days?,)?\s*(\d+:\d+|\d+ \w+)),.*(( \d.\d{2},?){3})/
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@cpu_arch = RbConfig::CONFIG['host_cpu']
|
14
|
+
@os = RbConfig::CONFIG['host_os'][/^[A-Za-z]+/]
|
15
|
+
|
16
|
+
uname = `uname -a`.match(/\w+ (\w[\w+\.\-]*) ([\w+\.\-]+)/)
|
17
|
+
@hostname, @os_version = uname[1], uname[2]
|
18
|
+
|
19
|
+
@platform = RUBY_PLATFORM
|
20
|
+
@ruby_version = RUBY_VERSION
|
21
|
+
@ruby_engine = RUBY_ENGINE
|
22
|
+
|
23
|
+
case os
|
24
|
+
when 'darwin'
|
25
|
+
cpu_info = `sysctl -n machdep.cpu.brand_string`.match(/^((\w+).*) @ (\d+.\d+)GHz/)
|
26
|
+
if cpu_info
|
27
|
+
@cpu_type = cpu_info[1]
|
28
|
+
@cpu_vendor = cpu_info[2].downcase.to_sym
|
29
|
+
@cpu_speed = Float(cpu_info[3])
|
30
|
+
end
|
31
|
+
|
32
|
+
@cpu_count = Integer(`sysctl hw.ncpu`[/\d+/])
|
33
|
+
os, release, build = `sw_vers`.scan(/:\t(.*)$/).flatten
|
34
|
+
@distribution = "#{os} #{release} (#{build})"
|
35
|
+
when 'linux'
|
36
|
+
cpu_info = File.read("/proc/cpuinfo")
|
37
|
+
|
38
|
+
@cpu_vendor = cpu_info[/vendor_id:\s+\s+(Genuine)?(\w+)/, 2]
|
39
|
+
model_name = cpu_info.match(/model name\s+:\s+((\w+).*) @ (\d+.\d+)GHz/)
|
40
|
+
if model_name
|
41
|
+
@cpu_type = model_name[1].gsub(/\s+/, ' ')
|
42
|
+
@cpu_vendor = model_name[2].downcase.to_sym
|
43
|
+
@cpu_speed = Float(model_name[3])
|
44
|
+
end
|
45
|
+
|
46
|
+
cores = cpu_info.scan(/core id\s+: \d+/).uniq.size
|
47
|
+
@cpu_count = cores > 0 ? cores : 1
|
48
|
+
@distribution = discover_distribution
|
49
|
+
else
|
50
|
+
@cpu_type = @cpu_vendor = @cpu_speed = @cpu_count = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
case RUBY_ENGINE
|
54
|
+
when 'ruby'
|
55
|
+
@ruby_platform = "ruby #{RUBY_VERSION}"
|
56
|
+
when 'jruby'
|
57
|
+
@ruby_platform = "jruby #{JRUBY_VERSION}"
|
58
|
+
when 'rbx'
|
59
|
+
@ruby_platform = "rbx #{Rubinius::VERSION}"
|
60
|
+
else
|
61
|
+
@ruby_platform = RUBY_ENGINE
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def platform; RUBY_PLATFORM; end
|
66
|
+
def ruby_engine; RUBY_ENGINE; end
|
67
|
+
def ruby_version; RUBY_VERSION; end
|
68
|
+
|
69
|
+
def load_averages(uptime_string = `uptime`)
|
70
|
+
matches = uptime_string.match(UPTIME_REGEX)
|
71
|
+
unless matches
|
72
|
+
Logger.warn "Couldn't parse uptime output: #{uptime_string}"
|
73
|
+
return
|
74
|
+
end
|
75
|
+
|
76
|
+
averages = matches[4].strip
|
77
|
+
averages.split(/,? /).map(&:to_f)
|
78
|
+
end
|
79
|
+
alias_method :load_average, :load_averages
|
80
|
+
|
81
|
+
def uptime(uptime_string = `uptime`)
|
82
|
+
matches = uptime_string.match(UPTIME_REGEX)
|
83
|
+
unless matches
|
84
|
+
Logger.warn "Couldn't parse uptime output: #{uptime_string}"
|
85
|
+
return
|
86
|
+
end
|
87
|
+
|
88
|
+
uptime = matches[1]
|
89
|
+
days_string = uptime[/^(\d+) days/, 1]
|
90
|
+
days_string ? Integer(days_string) : 0
|
91
|
+
end
|
92
|
+
|
93
|
+
def discover_distribution
|
94
|
+
`lsb_release -d`[/Description:\s+(.*)\s*$/, 1]
|
95
|
+
rescue Errno::ENOENT
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_hash
|
99
|
+
uptime_string = `uptime`
|
100
|
+
|
101
|
+
{
|
102
|
+
:os => os,
|
103
|
+
:os_version => os_version,
|
104
|
+
:hostname => hostname,
|
105
|
+
:platform => platform,
|
106
|
+
:distribution => distribution,
|
107
|
+
:ruby_version => ruby_version,
|
108
|
+
:ruby_engine => ruby_engine,
|
109
|
+
:ruby_platform => ruby_platform,
|
110
|
+
:load_averages => load_averages(uptime_string),
|
111
|
+
:uptime => uptime(uptime_string),
|
112
|
+
:cpu => {
|
113
|
+
:arch => cpu_arch,
|
114
|
+
:type => cpu_type,
|
115
|
+
:vendor => cpu_vendor,
|
116
|
+
:speed => cpu_speed,
|
117
|
+
:count => cpu_count
|
118
|
+
}
|
119
|
+
}
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module DCell
|
2
|
+
# A proxy object for a mailbox that delivers messages to the real mailbox on
|
3
|
+
# a remote node on a server far, far away...
|
4
|
+
class MailboxProxy
|
5
|
+
class InvalidNodeError < StandardError; end
|
6
|
+
|
7
|
+
def initialize(address)
|
8
|
+
mailbox_id, node_id = address.split("@")
|
9
|
+
|
10
|
+
# Create a proxy to the mailbox on the remote node
|
11
|
+
raise ArgumentError, "no mailbox_id given" unless mailbox_id
|
12
|
+
|
13
|
+
@node_id = node_id
|
14
|
+
@node = Node[node_id]
|
15
|
+
raise ArgumentError, "invalid node_id given" unless @node
|
16
|
+
|
17
|
+
@mailbox_id = mailbox_id
|
18
|
+
end
|
19
|
+
|
20
|
+
# name@host style address
|
21
|
+
def address
|
22
|
+
"#{@mailbox_id}@#{@node_id}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def inspect
|
26
|
+
"#<DCell::MailboxProxy:0x#{object_id.to_s(16)} #{address}>"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Send a message to the mailbox
|
30
|
+
def <<(message)
|
31
|
+
@node.async.send_message Message::Relay.new(self, message)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Is the remote mailbox still alive?
|
35
|
+
def alive?
|
36
|
+
true # FIXME: hax!
|
37
|
+
end
|
38
|
+
|
39
|
+
# Custom marshaller for compatibility with Celluloid::Mailbox marshalling
|
40
|
+
def _dump(level)
|
41
|
+
"#{@mailbox_id}@#{@node_id}"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Loader for custom marshal format
|
45
|
+
def self._load(address)
|
46
|
+
if mailbox = DCell::Router.find(address)
|
47
|
+
mailbox
|
48
|
+
else
|
49
|
+
DCell::MailboxProxy.new(address)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module DCell
|
2
|
+
class Message
|
3
|
+
attr_reader :id
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
# Memoize the original object ID so it will get marshalled
|
7
|
+
# Perhaps this should use a real UUID scheme
|
8
|
+
@id = object_id
|
9
|
+
end
|
10
|
+
|
11
|
+
# Heartbeat messages inform other nodes this node is healthy
|
12
|
+
class Heartbeat < Message
|
13
|
+
def initialize
|
14
|
+
@id = DCell.id
|
15
|
+
end
|
16
|
+
|
17
|
+
def dispatch
|
18
|
+
node = DCell::Node[@id]
|
19
|
+
node.handle_heartbeat if node
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Query a node for the address of an actor
|
24
|
+
class Find < Message
|
25
|
+
attr_reader :sender, :name
|
26
|
+
|
27
|
+
def initialize(sender, name)
|
28
|
+
super()
|
29
|
+
@sender, @name = sender, name
|
30
|
+
end
|
31
|
+
|
32
|
+
def dispatch
|
33
|
+
@sender << SuccessResponse.new(@id, Celluloid::Actor[@name])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# List all registered actors
|
38
|
+
class List < Message
|
39
|
+
attr_reader :sender
|
40
|
+
|
41
|
+
def initialize(sender)
|
42
|
+
super()
|
43
|
+
@sender = sender
|
44
|
+
end
|
45
|
+
|
46
|
+
def dispatch
|
47
|
+
@sender << SuccessResponse.new(@id, Celluloid::Actor.registered)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Relay a message to the given recipient
|
52
|
+
class Relay < Message
|
53
|
+
attr_reader :recipient, :message
|
54
|
+
|
55
|
+
def initialize(recipient, message)
|
56
|
+
super()
|
57
|
+
@recipient, @message = recipient, message
|
58
|
+
end
|
59
|
+
|
60
|
+
def dispatch
|
61
|
+
@recipient << @message
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/dcell/node.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
module DCell
|
2
|
+
# A node in a DCell cluster
|
3
|
+
class Node
|
4
|
+
include Celluloid
|
5
|
+
include Celluloid::FSM
|
6
|
+
attr_reader :id, :addr
|
7
|
+
|
8
|
+
finalizer :shutdown
|
9
|
+
|
10
|
+
NODE_DISCOVERY_TIMEOUT = 5
|
11
|
+
|
12
|
+
# FSM
|
13
|
+
default_state :disconnected
|
14
|
+
state :shutdown
|
15
|
+
state :disconnected, :to => [:connected, :shutdown]
|
16
|
+
state :connected do
|
17
|
+
send_heartbeat
|
18
|
+
Celluloid::Logger.info "Connected to #{id}"
|
19
|
+
end
|
20
|
+
state :partitioned do
|
21
|
+
@heartbeat.cancel
|
22
|
+
Celluloid::Logger.warn "Communication with #{id} interrupted"
|
23
|
+
end
|
24
|
+
|
25
|
+
@nodes = {}
|
26
|
+
@lock = Mutex.new
|
27
|
+
|
28
|
+
@heartbeat_rate = 5 # How often to send heartbeats in seconds
|
29
|
+
@heartbeat_timeout = 10 # How soon until a lost heartbeat triggers a node partition
|
30
|
+
|
31
|
+
# Singleton methods
|
32
|
+
class << self
|
33
|
+
include Enumerable
|
34
|
+
extend Forwardable
|
35
|
+
|
36
|
+
def_delegators "Celluloid::Actor[:node_manager]", :all, :each, :find, :[], :remove, :update
|
37
|
+
def_delegators "Celluloid::Actor[:node_manager]", :heartbeat_rate, :heartbeat_timeout
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize(id, addr)
|
41
|
+
@id, @addr = id, addr
|
42
|
+
@socket = nil
|
43
|
+
@heartbeat = nil
|
44
|
+
|
45
|
+
# Total hax to accommodate the new Celluloid::FSM API
|
46
|
+
attach self
|
47
|
+
end
|
48
|
+
|
49
|
+
def update_client_address( addr )
|
50
|
+
@addr = addr
|
51
|
+
send_heartbeat
|
52
|
+
end
|
53
|
+
|
54
|
+
def update_server_address(addr)
|
55
|
+
@addr = addr
|
56
|
+
end
|
57
|
+
|
58
|
+
def shutdown
|
59
|
+
transition :shutdown
|
60
|
+
@socket.close if @socket
|
61
|
+
end
|
62
|
+
|
63
|
+
# Obtain the node's 0MQ socket
|
64
|
+
def socket
|
65
|
+
return @socket if @socket
|
66
|
+
|
67
|
+
@socket = Celluloid::ZMQ::PushSocket.new
|
68
|
+
begin
|
69
|
+
@socket.connect addr
|
70
|
+
rescue IOError
|
71
|
+
@socket.close
|
72
|
+
@socket = nil
|
73
|
+
raise
|
74
|
+
end
|
75
|
+
|
76
|
+
transition :connected
|
77
|
+
@socket
|
78
|
+
end
|
79
|
+
|
80
|
+
# Find an actor registered with a given name on this node
|
81
|
+
def find(name)
|
82
|
+
request = Message::Find.new(Thread.mailbox, name)
|
83
|
+
send_message request
|
84
|
+
|
85
|
+
response = receive(NODE_DISCOVERY_TIMEOUT) do |msg|
|
86
|
+
msg.respond_to?(:request_id) && msg.request_id == request.id
|
87
|
+
end
|
88
|
+
|
89
|
+
return nil if response.nil?
|
90
|
+
abort response.value if response.is_a? ErrorResponse
|
91
|
+
response.value
|
92
|
+
end
|
93
|
+
alias_method :[], :find
|
94
|
+
|
95
|
+
# List all registered actors on this node
|
96
|
+
def actors
|
97
|
+
request = Message::List.new(Thread.mailbox)
|
98
|
+
send_message request
|
99
|
+
|
100
|
+
response = receive do |msg|
|
101
|
+
msg.respond_to?(:request_id) && msg.request_id == request.id
|
102
|
+
end
|
103
|
+
|
104
|
+
abort response.value if response.is_a? ErrorResponse
|
105
|
+
response.value
|
106
|
+
end
|
107
|
+
alias_method :all, :actors
|
108
|
+
|
109
|
+
# Send a message to another DCell node
|
110
|
+
def send_message(message)
|
111
|
+
begin
|
112
|
+
message = Marshal.dump(message)
|
113
|
+
rescue => ex
|
114
|
+
abort ex
|
115
|
+
end
|
116
|
+
|
117
|
+
socket << message
|
118
|
+
end
|
119
|
+
alias_method :<<, :send_message
|
120
|
+
|
121
|
+
# Send a heartbeat message after the given interval
|
122
|
+
def send_heartbeat
|
123
|
+
send_message DCell::Message::Heartbeat.new
|
124
|
+
@heartbeat = after(self.class.heartbeat_rate) { send_heartbeat }
|
125
|
+
end
|
126
|
+
|
127
|
+
# Handle an incoming heartbeat for this node
|
128
|
+
def handle_heartbeat
|
129
|
+
transition :connected
|
130
|
+
transition :partitioned, :delay => self.class.heartbeat_timeout
|
131
|
+
end
|
132
|
+
|
133
|
+
# Friendlier inspection
|
134
|
+
def inspect
|
135
|
+
"#<DCell::Node[#{@id}] @addr=#{@addr.inspect}>"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module DCell
|
2
|
+
# Manage nodes we're connected to
|
3
|
+
class NodeManager
|
4
|
+
include Celluloid::ZMQ
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
trap_exit :node_died
|
8
|
+
|
9
|
+
attr_reader :heartbeat_rate, :heartbeat_timeout
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@nodes = {}
|
13
|
+
|
14
|
+
@heartbeat_rate = 5 # How often to send heartbeats in seconds
|
15
|
+
@heartbeat_timeout = 10 # How soon until a lost heartbeat triggers a node partition
|
16
|
+
end
|
17
|
+
|
18
|
+
# Return all available nodes in the cluster
|
19
|
+
def all
|
20
|
+
Directory.all.map do |node_id|
|
21
|
+
find node_id
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Iterate across all available nodes
|
26
|
+
def each
|
27
|
+
Directory.all.each do |node_id|
|
28
|
+
yield find node_id
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Find a node by its node ID
|
33
|
+
def find(id)
|
34
|
+
node = @nodes[id]
|
35
|
+
return node if node
|
36
|
+
|
37
|
+
addr = Directory[id]
|
38
|
+
return unless addr
|
39
|
+
|
40
|
+
if id == DCell.id
|
41
|
+
node = DCell.me
|
42
|
+
else
|
43
|
+
node = Node.new(id, addr)
|
44
|
+
self.link node
|
45
|
+
end
|
46
|
+
|
47
|
+
@nodes[id] ||= node
|
48
|
+
@nodes[id]
|
49
|
+
end
|
50
|
+
alias_method :[], :find
|
51
|
+
|
52
|
+
def node_died(node, reason)
|
53
|
+
if reason.nil? # wtf?
|
54
|
+
# this wtf error seems to come from node socket writes
|
55
|
+
# when the socket is not reachable anymore
|
56
|
+
Celluloid::logger.debug "wtf?"
|
57
|
+
return
|
58
|
+
end
|
59
|
+
# Handle dead node???
|
60
|
+
end
|
61
|
+
|
62
|
+
def update(id)
|
63
|
+
addr = Directory[id]
|
64
|
+
return unless addr
|
65
|
+
if ( node = @nodes[id] ) and node.alive?
|
66
|
+
node.update_client_address( addr )
|
67
|
+
else
|
68
|
+
@nodes[id] = Node.new( id, addr )
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def remove(id)
|
73
|
+
if @nodes[id]
|
74
|
+
@nodes[id].terminate if @nodes[id].alive?
|
75
|
+
@nodes.delete(id)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|