Skip to content

Commit

Permalink
WIP: Refactor the Registry API (celluloid#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
aflatter committed Jul 10, 2013
1 parent 66d9af4 commit b51d622
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 108 deletions.
9 changes: 6 additions & 3 deletions lib/dcell/directory.rb
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
module DCell
# Directory of nodes connected to the DCell cluster
#
# WARNING: This is an internal API and kept for backwards-compatibility.
# It may be removed at some point.
module Directory
extend self

# Get the URL for a particular Node ID
def get(node_id)
DCell.registry.get_node node_id
DCell.registry.get(:nodes, node_id)
end
alias_method :[], :get

# Set the address of a particular Node ID
def set(node_id, addr)
DCell.registry.set_node node_id, addr
DCell.registry.set(:nodes, node_id, addr)
end
alias_method :[]=, :set

# List all of the node IDs in the directory
def all
DCell.registry.nodes
DCell.registry.keys(:nodes)
end
end
end
6 changes: 3 additions & 3 deletions lib/dcell/global.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ module Global

# Get a global value
def get(key)
DCell.registry.get_global key.to_s
DCell.registry.get(:global, key)
end
alias_method :[], :get

# Set a global value
def set(key, value)
DCell.registry.set_global key.to_s, value
DCell.registry.set(:global, key, value)
end
alias_method :[]=, :set

# Get the keys for all the globals in the system
def keys
DCell.registry.global_keys.map(&:to_sym)
DCell.registry.keys(:global).map(&:to_sym)
end
end
end
25 changes: 25 additions & 0 deletions lib/dcell/registries/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
class DCell::Registry::Base
include Celluloid

def get(namespace, key)
abort NotImplementedError.new
end
alias_method :[], :get

def set(namespace, key, value)
abort NotImplementedError.new
end
alias_method :[]=, :set

def delete(namespace, key)
abort NotImplementedError.new
end

def keys(namespace)
abort NotImplementedError.new
end

def clear(namespace)
abort NotImplementedError.new
end
end
79 changes: 18 additions & 61 deletions lib/dcell/registries/redis_adapter.rb
Original file line number Diff line number Diff line change
@@ -1,87 +1,44 @@
require 'celluloid/redis'
require 'redis/connection/celluloid'
require 'redis-namespace'
require 'dcell/registry'
require 'dcell/registries/base'

module DCell
module Registry
class RedisAdapter
class RedisAdapter < Registry::Base
def initialize(options)
# Convert all options to symbols :/
options = options.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }

@env = options[:env] || 'production'
@namespace = options[:namespace] || "dcell_#{@env}"

redis = Redis.new options
@redis = Redis::Namespace.new @namespace, :redis => redis

@node_registry = NodeRegistry.new(@redis)
@global_registry = GlobalRegistry.new(@redis)
redis = ::Redis.new options
@redis = ::Redis::Namespace.new @namespace, :redis => redis
end

def clear_nodes
@node_registry.clear
def get(namespace, key)
string = @redis.hget(namespace, key.to_s)
Marshal.load(string) if string
end

def clear_globals
@global_registry.clear
def set(namespace, key, value)
string = Marshal.dump(value)
@redis.hset(namespace, key.to_s, string)
end

class NodeRegistry
def initialize(redis)
@redis = redis
end

def get(node_id)
@redis.hget 'nodes', node_id
end

def set(node_id, addr)
@redis.hset 'nodes', node_id, addr
end

def nodes
@redis.hkeys 'nodes'
end

def clear
@redis.del 'nodes'
end
def delete(namespace, key)
@redis.hdel(namespace, key.to_s)
end

def get_node(node_id); @node_registry.get(node_id) end
def set_node(node_id, addr); @node_registry.set(node_id, addr) end
def nodes; @node_registry.nodes end

class GlobalRegistry
def initialize(redis)
@redis = redis
end

def get(key)
string = @redis.hget 'globals', key.to_s
Marshal.load string if string
end

# Set a global value
def set(key, value)
string = Marshal.dump value
@redis.hset 'globals', key.to_s, string
end

# The keys to all globals in the system
def global_keys
@redis.hkeys 'globals'
end

def clear
@redis.del 'globals'
end
def keys(namespace)
@redis.hkeys(namespace)
end

def get_global(key); @global_registry.get(key) end
def set_global(key, value); @global_registry.set(key, value) end
def global_keys; @global_registry.global_keys end
def clear(namespace)
@redis.del(namespace)
end
end
end
end
64 changes: 23 additions & 41 deletions lib/dcell/registries/zk_adapter.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
require 'zk'
require 'dcell/registry'
require 'dcell/registries/base'

module DCell
module Registry
class ZkAdapter
class ZkAdapter < Base
PREFIX = "/dcell"
DEFAULT_PORT = 2181

Expand Down Expand Up @@ -36,52 +38,32 @@ def initialize(options)
end

@zk = ZK.new(*servers)
@node_registry = Registry.new(@zk, @base_path, :nodes, true)
@global_registry = Registry.new(@zk, @base_path, :globals, false)
end

class Registry
def initialize(zk, base_path, name, ephemeral)
@zk = zk
@base_path = File.join(base_path, name.to_s)
@ephemeral = ephemeral
@zk.mkdir_p @base_path
end

def get(key)
result, _ = @zk.get("#{@base_path}/#{key}")
Marshal.load result
rescue ZK::Exceptions::NoNode
end

def set(key, value)
path = "#{@base_path}/#{key}"
string = Marshal.dump value
@zk.set path, string
rescue ZK::Exceptions::NoNode
@zk.create path, string, :ephemeral => @ephemeral
end

def all
@zk.children @base_path
end

def clear
@zk.rm_rf @base_path
@zk.mkdir_p @base_path
end
def get(namespace, key)
path = File.join(@base_path, namespace.to_s, key)
result, _ = @zk.get(path)
Marshal.load(result)
rescue ZK::Exceptions::NoNode
end

def get_node(node_id); @node_registry.get(node_id) end
def set_node(node_id, addr); @node_registry.set(node_id, addr) end
def nodes; @node_registry.all end
def clear_nodes; @node_registry.clear end
def set(namespace, key, value)
path = File.join(@base_path, namespace.to_s, key)
string = Marshal.dump(value)
@zk.set(path, string)
rescue ZK::Exceptions::NoNode
@zk.create(path, string, ephemeral: (namespace == :nodes))
end

def get_global(key); @global_registry.get(key) end
def set_global(key, value); @global_registry.set(key, value) end
def global_keys; @global_registry.all end
def clear_globals; @global_registry.clear end
def keys(namespace)
@zk.children(File.join(@base_path, namespace.to_s))
end

def clear(namespace)
path = File.join(@base_path, namespace.to_s)
@zk.rm_rf(path)
@zk.mkdir_p(path)
end
end
end
end
2 changes: 2 additions & 0 deletions lib/dcell/registry.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module DCell::Registry
end

4 comments on commit b51d622

@spangenberg
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if the marshalling is too much overhead.

@aflatter
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Neonlex I think it isn't, Marshal is quite fast. Do you have an application that you can profile?

@spangenberg
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aflatter I could try it but right now I don't have much time. I only wrote this benchmark https://gist.github.com/neonlex/5974704 the result was:

Write:
                       user     system      total        real
without marshal:  10.710000   0.160000  10.870000 ( 10.915371)
with marshal:     19.620000   0.190000  19.810000 ( 19.824648)

Read:
                       user     system      total        real
without marshal:   0.480000   0.030000   0.510000 (  0.509705)
with marshal:      3.560000   0.070000   3.630000 (  3.627300)

I have to think a bit about this and I will try to run this over the weekend on my application.

@aflatter
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Neonlex Yes, please benchmark in the context of a DCell application.

Please sign in to comment.