Skip to content

Commit

Permalink
Initial import
Browse files Browse the repository at this point in the history
  • Loading branch information
acrosa committed Mar 5, 2010
0 parents commit 70fcd36
Show file tree
Hide file tree
Showing 22 changed files with 1,034 additions and 0 deletions.
Empty file added CHANGELOG
Empty file.
20 changes: 20 additions & 0 deletions MIT-LICENSE
@@ -0,0 +1,20 @@
Copyright (c) 2010 Alejandro Crosa

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
45 changes: 45 additions & 0 deletions README.md
@@ -0,0 +1,45 @@
voldemort-rb
================

# Requirements

Since the communication between the client and the server is done using protocol buffers you'll need the ruby_protobuf gem found at http://code.google.com/p/ruby-protobuf/.

Examples
=======

# Basic Usage
## Connecting and bootstrapping

client = VoldemortClient.new("test", "localhost:6666")

## Storing a value

client.put("some key", "some value")

## Reading a value

client.get("some key")

you'll get

=> some value

## deleting a value from a key

client.delete("some key")

# Conflict resolution
## Default

Voldemort replies with versions of a value, it's up to the client to resolve the conflicts. By default the library will return the version that's most recent.

## Custom

You can override the default behavior and perform a custom resolution of the conflict, here's how to do so:

client = VoldemortClient.new("test", "localhost:6666") do |versions|
versions.first # just return the first version for example
end

Copyright (c) 2010 Alejandro Crosa, released under the MIT license
60 changes: 60 additions & 0 deletions Rakefile
@@ -0,0 +1,60 @@
require 'rubygems'
require 'rake/gempackagetask'
require 'rubygems/specification'
require 'date'
require 'spec/rake/spectask'

GEM = 'Voldemort Client'
GEM_NAME = 'voldemort_client'
GEM_VERSION = '0.1'
AUTHORS = ['Alejandro Crosa']
EMAIL = "alejandrocrosa@gmail.com"
HOMEPAGE = "http://github.com/acrosa/Voldemort-Ruby-Client"
SUMMARY = "A Ruby client for the Voldemort distributed key value store"

spec = Gem::Specification.new do |s|
s.name = GEM
s.version = GEM_VERSION
s.platform = Gem::Platform::RUBY
s.has_rdoc = true
s.extra_rdoc_files = ["LICENSE"]
s.summary = SUMMARY
s.description = s.summary
s.authors = AUTHORS
s.email = EMAIL
s.homepage = HOMEPAGE
s.add_development_dependency "rspec"
s.require_path = 'lib'
s.autorequire = GEM
s.files = %w(LICENSE README.md Rakefile) + Dir.glob("{lib,tasks,spec}/**/*")
end

task :default => :spec

desc "Run specs"
Spec::Rake::SpecTask.new do |t|
t.spec_files = FileList['spec/**/*_spec.rb']
t.spec_opts = %w(-fs --color)
end

Rake::GemPackageTask.new(spec) do |pkg|
pkg.gem_spec = spec
end

desc "install the gem locally"
task :install => [:package] do
sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
end

desc "create a gemspec file"
task :make_spec do
File.open("#{GEM}.gemspec", "w") do |file|
file.puts spec.to_ruby
end
end

desc "Run all examples with RCov"
Spec::Rake::SpecTask.new(:rcov) do |t|
t.spec_files = FileList['spec/**/*_spec.rb']
t.rcov = true
end
1 change: 1 addition & 0 deletions install.rb
@@ -0,0 +1 @@
# Install hook code here
115 changes: 115 additions & 0 deletions lib/connection/connection.rb
@@ -0,0 +1,115 @@
require 'rexml/document'

class Connection
include REXML

attr_accessor :hosts # The hosts from where we bootstrapped.
attr_accessor :nodes # The array of VoldemortNodes available.
attr_accessor :db_name # The DB store name.
attr_accessor :connected_node # The VoldemortNode we are connected to.
attr_accessor :request_count # Used to track the number of request a node receives.
attr_accessor :request_limit_per_node # Limit the number of request per node.

STATUS_OK = "ok"
PROTOCOL = "pb0"
DEFAULT_REQUEST_LIMIT_PER_NODE = 500

def initialize(db_name, hosts, request_limit_per_node = DEFAULT_REQUEST_LIMIT_PER_NODE)
self.db_name = db_name
self.hosts = hosts
self.nodes = hosts.collect{ |h|
n = h.split(":")
node = VoldemortNode.new
node.host = n[0]
node.port = n[1]
node
}
self.request_count = 0
self.request_limit_per_node = request_limit_per_node
end

def bootstrap
response = self.get_from("metadata", "cluster.xml", false)
xml = response[1][0][1]
self.nodes = self.parse_nodes_from(xml)
self.connect_to_random_node
rescue StandardError => e
raise("There was an error trying to bootstrap from the specified servers: #{e}")
end

def connect_to_random_node
nodes = self.nodes.sort_by { rand }
for node in nodes do
if self.connect_to(node.host, node.port)
self.connected_node = node
self.request_count = 0
return node
end
end
end

def parse_nodes_from(xml)
nodes = []
doc = REXML::Document.new(xml)
XPath.each(doc, "/cluster/server").each do |n|
node = VoldemortNode.new
node.id = n.elements["id"].text
node.host = n.elements["host"].text
node.port = n.elements["socket-port"].text
node.http_port = n.elements["http-port"].text
node.admin_port = n.elements["admin-port"].text
node.partitions = n.elements["partitions"].text
nodes << node
end
nodes
end

def protocol_version
PROTOCOL
end

def connect
self.connect!
end

def reconnect
self.reconnect!
end

def disconnect
self.disconnect!
end

def reconnect_when_errors_in(response = nil)
return unless response
self.reconnect! if response.error
end

def rebalance_connection?
self.request_count >= self.request_limit_per_node
end

def rebalance_connection_if_needed
self.reconnect if self.rebalance_connection?
self.request_count += 1
end

def get(key)
self.rebalance_connection_if_needed
self.get_from(self.db_name, key, true)
end

def get_all(keys)
self.rebalance_connection_if_needed
self.get_all_from(self.db_name, keys, true)
end

def put(key, value, version = nil, route = true)
self.rebalance_connection_if_needed
self.put_from(self.db_name, key, value, version, route)
end

def delete(key)
self.delete_from(self.db_name, key)
end
end

0 comments on commit 70fcd36

Please sign in to comment.