Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include voldemort-rb on default install #43

Merged
3 commits merged into from Feb 26, 2011
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Empty file added clients/ruby/CHANGELOG
Empty file.
20 changes: 20 additions & 0 deletions clients/ruby/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.
64 changes: 64 additions & 0 deletions clients/ruby/README.md
@@ -0,0 +1,64 @@
voldemort-rb
================

# Installing the Gem from rubygems

> sudo gem install 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/.

sudo gem install ruby_protobuf

XML Parsing is done using Nokogiri

sudo gem install nokogiri

# Building and Installing the Gem from source

> gem build voldemort-rb.gemspec

> sudo gem install voldemort-rb-0.1.X.gem (replace 'X' with the correct version)

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 clients/ruby/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.5'
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 clients/ruby/install.rb
@@ -0,0 +1 @@
# Install hook code here
147 changes: 147 additions & 0 deletions clients/ruby/lib/connection/connection.rb
@@ -0,0 +1,147 @@
require 'nokogiri'

class Connection

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.
attr_accessor :key_serializer_schemas
attr_accessor :value_serializer_schemas
attr_accessor :key_serializer_type
attr_accessor :value_serializer_type

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
cluster_response = self.get_from("metadata", "cluster.xml", false)
cluster_xml_doc = Nokogiri::XML(cluster_response[1][0][1])
self.nodes = self.parse_nodes_from(cluster_xml_doc)

stores_response = self.get_from("metadata", "stores.xml", false)

stores_xml = stores_response[1][0][1]

doc = Nokogiri::XML(stores_xml)

self.key_serializer_type = self.parse_schema_type(doc, 'key-serializer')
self.value_serializer_type = self.parse_schema_type(doc, 'value-serializer')
self.key_serializer_schemas = self.parse_schema_from(doc, 'key-serializer')
self.value_serializer_schemas = self.parse_schema_from(doc, 'value-serializer')

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_schema_type(doc, serializer = 'value-serializer')
type_doc = doc.xpath("//stores/store[name = \"#{self.db_name}\"]/#{serializer}/type")
if(type_doc != nil)
return type_doc.text
else
return nil
end
end

def parse_schema_from(doc, serializer = 'value-serializer')
parsed_schemas = {}
doc.xpath("//stores/store[name = \"#{self.db_name}\"]/#{serializer}/schema-info").each do |value_serializer|
parsed_schemas[value_serializer.attributes['version'].text] = value_serializer.text
end
return parsed_schemas
end

def parse_nodes_from(doc)
nodes = []
doc.xpath("/cluster/server").each do |n|
node = VoldemortNode.new
node.id = n.xpath("//id").text
node.host = n.xpath("//host").text
node.port = n.xpath("//socket-port").text
node.http_port = n.xpath("//http_port").text
node.admin_port = n.xpath("//admin-port").text
node.partitions = n.xpath("//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