roma-client 0.3.7

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.
data/README ADDED
@@ -0,0 +1,17 @@
1
+ ================================================================
2
+ ROMA: A Distributed Key-Value Store in Ruby
3
+ Copyright (C) 2009 Rakuten, Inc.
4
+ ================================================================
5
+
6
+ ROMA is one of the data storing systems for distributed key-value
7
+ stores. It is a completely decentralized distributed system that
8
+ consists of multiple processes, called nodes, on several machines. It
9
+ is based on pure P2P architecture like a distributed hash table, thus
10
+ it provides high availability and scalability.
11
+
12
+ ROMA is written in Ruby. However, following choices are available to
13
+ access to ROMA.
14
+
15
+ * Client libraries of Ruby and Java are available.
16
+ * ROMA protocol is compatible with memcached text-based one so that
17
+ any memcached client libraries allows users to interact with ROMA.
data/Rakefile ADDED
@@ -0,0 +1,59 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'rubygems'
3
+ require 'rake'
4
+ require 'rake/gempackagetask'
5
+ require 'rake/rdoctask'
6
+
7
+ RDOC_OPTIONS = [
8
+ '--line-numbers',
9
+ '--inline-source',
10
+ "--main", "README.rdoc",
11
+ "-c UTF-8",
12
+ ]
13
+
14
+ # gem tasks
15
+ PKG_FILES = FileList[
16
+ '[A-Z]*',
17
+ 'bin/**/*',
18
+ 'lib/**/*.rb',
19
+ 'test/**/*.rb',
20
+ 'spec/**/*.rb',
21
+ 'doc/**/*',
22
+ 'examples/**/*',
23
+ ]
24
+
25
+ VER_NUM = `ruby -Ilib -e 'require "roma/client/version"; puts Roma::Client::VERSION::STRING'`
26
+
27
+ if VER_NUM =~ /([0-9.]+)$/
28
+ CURRENT_VERSION = $1
29
+ else
30
+ CURRENT_VERSION = "0.0.0"
31
+ end
32
+
33
+ SPEC = Gem::Specification.new do |s|
34
+ s.name = "roma-client"
35
+ s.version = CURRENT_VERSION
36
+ s.summary = "ROMA client library"
37
+ s.description = <<-EOF
38
+ ROMA client library
39
+ EOF
40
+ s.files = PKG_FILES.to_a
41
+
42
+ s.require_path = 'lib' # Use these for libraries.
43
+
44
+ s.has_rdoc = true
45
+ s.rdoc_options.concat RDOC_OPTIONS
46
+ s.extra_rdoc_files = ["README"]
47
+ end
48
+
49
+ package_task = Rake::GemPackageTask.new(SPEC) do |pkg|
50
+ end
51
+
52
+
53
+ Rake::RDocTask.new("doc") { |rdoc|
54
+ rdoc.rdoc_dir = 'doc'
55
+ rdoc.title = "ROMA documents"
56
+ rdoc.options.concat RDOC_OPTIONS
57
+ rdoc.rdoc_files.include('lib/**/*.rb')
58
+ rdoc.rdoc_files.include("README.rdoc")
59
+ }
data/bin/rcdaemon ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ path = File.dirname(File.expand_path($PROGRAM_NAME))
4
+ $LOAD_PATH << path + "/../lib"
5
+
6
+ require 'roma/client/proxy/daemon'
data/bin/sample ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ path = File.dirname(File.expand_path($PROGRAM_NAME))
4
+ $LOAD_PATH << path + "/../lib"
5
+
6
+ if ARGV.length < 1
7
+ puts "usage:sample address:port"
8
+ exit
9
+ end
10
+
11
+ require 'roma/client'
12
+
13
+ rc = Roma::Client::RomaClient.new(ARGV)
14
+
15
+ n = 10
16
+
17
+ n.times{|i|
18
+ rc["key-#{i}"]="value-#{i}"
19
+ }
20
+
21
+ n.times{|i|
22
+ puts rc["key-#{i}"]
23
+ }
data/bin/showbalance ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ path = File.dirname(File.expand_path($PROGRAM_NAME))
4
+ $LOAD_PATH << path + "/../lib"
5
+
6
+ require 'roma/client/tools/showbalance'
@@ -0,0 +1,77 @@
1
+ require 'yaml'
2
+ require 'digest/sha1'
3
+ require 'roma/client/routing/routing_data'
4
+
5
+ module Roma
6
+ module Client
7
+
8
+ class ClientRoutingTable
9
+
10
+ attr :rd
11
+ attr :search_mask
12
+ attr :fail_cnt
13
+ attr :hbits
14
+ attr :rn
15
+ attr :div_bits
16
+ attr_accessor :mklhash
17
+
18
+ def initialize(rd)
19
+ @rd = rd
20
+ @rn = @rd.rn
21
+ @div_bits=@rd.div_bits
22
+ @hbits = 2**@rd.dgst_bits
23
+ @search_mask = @rd.search_mask
24
+ @fail_cnt = Hash.new(0)
25
+ @mklhash = nil
26
+ end
27
+
28
+ def nodes
29
+ @rd.nodes.clone
30
+ end
31
+
32
+ def vnodes
33
+ @rd.v_idx.keys
34
+ end
35
+
36
+ # Returns a vnode-id from digest.
37
+ def get_vnode_id(d)
38
+ d & @search_mask
39
+ end
40
+
41
+ # Returns a node-is list at the vnode.
42
+ # +vn+: vnode-id
43
+ def search_nodes(vn)
44
+ @rd.v_idx[vn].clone
45
+ rescue
46
+ nil
47
+ end
48
+
49
+ def search_node(key)
50
+ d = Digest::SHA1.hexdigest(key).hex % @hbits
51
+ nodes = @rd.v_idx[d & @search_mask]
52
+ nodes.each_index { |i|
53
+ return [nodes[i], d] if @fail_cnt[nodes[i]] == 0
54
+ }
55
+ # for expecting an auto assign process
56
+ svn = vn = d & @search_mask
57
+ while( (vn = @rd.next_vnode(vn)) != svn )
58
+ nodes = @rd.v_idx[vn]
59
+ nodes.each_index { |i|
60
+ return [nodes[i], d] if @fail_cnt[nodes[i]] == 0
61
+ }
62
+ end
63
+ nil
64
+ rescue => e
65
+ p e
66
+ nil
67
+ end
68
+
69
+ def proc_failed(nid)
70
+ @fail_cnt[nid] += 1
71
+ @mklhash = 0
72
+ end
73
+
74
+ end # class ClientRoutingTable
75
+
76
+ end # module Client
77
+ end # module Roma
@@ -0,0 +1,76 @@
1
+ require 'thread'
2
+ require 'socket'
3
+ require 'singleton'
4
+
5
+ module Roma
6
+ module Client
7
+
8
+ class ConPool
9
+ include Singleton
10
+
11
+ attr_accessor :maxlength
12
+
13
+ def initialize(maxlength = 10)
14
+ @pool = {}
15
+ @maxlength = maxlength
16
+ @lock = Mutex.new
17
+ end
18
+
19
+ def get_connection(ap)
20
+ ret = @pool[ap].shift if @pool.key?(ap) && @pool[ap].length > 0
21
+ ret = create_connection(ap) unless ret
22
+ ret
23
+ rescue
24
+ nil
25
+ end
26
+
27
+ def return_connection(ap, con)
28
+ if @pool.key?(ap) && @pool[ap].length > 0
29
+ if @pool[ap].length > @maxlength
30
+ con.close
31
+ else
32
+ @pool[ap] << con
33
+ end
34
+ else
35
+ @pool[ap] = [con]
36
+ end
37
+ rescue
38
+ end
39
+
40
+ def create_connection(ap)
41
+ addr, port = ap.split(/[:_]/)
42
+ TCPSocket.new(addr, port)
43
+ end
44
+
45
+ def delete_connection(ap)
46
+ @pool.delete(ap)
47
+ end
48
+
49
+ def close_all
50
+ @pool.each_key{|ap| close_at(ap) }
51
+ end
52
+
53
+ def close_same_host(ap)
54
+ host,port = ap.split(/[:_]/)
55
+ @pool.each_key{|eap|
56
+ close_at(eap) if eap.split(/[:_]/)[0] == host
57
+ }
58
+ end
59
+
60
+ def close_at(ap)
61
+ return unless @pool.key?(ap)
62
+ @lock.synchronize {
63
+ while(@pool[ap].length > 0)
64
+ begin
65
+ @pool[ap].shift.close
66
+ rescue =>e
67
+ end
68
+ end
69
+ @pool.delete(ap)
70
+ }
71
+ end
72
+
73
+ end # class ConPool
74
+
75
+ end # module Client
76
+ end # module Roma
@@ -0,0 +1,281 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # client proxy daemon
4
+ #
5
+ require 'optparse'
6
+ require 'eventmachine'
7
+ require 'timeout'
8
+ require 'singleton'
9
+ require 'roma/client/sender'
10
+ require 'roma/client/rlogger'
11
+ require 'roma/client/client_rttable'
12
+
13
+ module Roma
14
+ module Client
15
+ module Proxy
16
+
17
+ module RomaHandler
18
+ attr_accessor :ap
19
+ attr_reader :connected
20
+
21
+ def post_init
22
+ $log.info("Connected to roma")
23
+ @connected = true
24
+ end
25
+
26
+ def unbind
27
+ $log.info("Disconnected from roma")
28
+ @connected = nil
29
+ end
30
+ end # module RomaHandler
31
+
32
+ module ClientHandler
33
+
34
+ def post_init
35
+ $log.info("Connected from client")
36
+ @cmd = ''
37
+ end
38
+
39
+ def receive_data(data)
40
+ @cmd << data
41
+ if @cmd.index("\n")
42
+ @roma_h = get_roma_handler(@cmd)
43
+ @roma_h.send_data(@cmd)
44
+ @cmd = ''
45
+ EM::enable_proxy(self, @roma_h)
46
+ EM::enable_proxy(@roma_h, self)
47
+ end
48
+ rescue =>e
49
+ $log.error("#{e} #{$@}")
50
+ end
51
+
52
+ def unbind
53
+ $log.info("Disconnected from client")
54
+ EM::disable_proxy(self)
55
+ if @roma_h
56
+ EM::disable_proxy(@roma_h)
57
+ Conpool::instance.return_connection(@roma_h)
58
+ end
59
+ rescue =>e
60
+ $log.error("#{e} #{$@}")
61
+ end
62
+
63
+ def get_roma_handler(cmd_line)
64
+ cmd, key_hname = cmd_line.split(' ')
65
+ key, hname = key_hname.split("\e")
66
+ nid, d = Daemon::rttable.search_node(key)
67
+ Conpool::instance.get_connection(nid, RomaHandler)
68
+ rescue =>e
69
+ $log.error("#{e} #{$@}")
70
+ end
71
+
72
+ end # module ClientHandler
73
+
74
+ class Conpool
75
+ include Singleton
76
+
77
+ attr_accessor :maxlength
78
+
79
+ def initialize
80
+ @pool = {}
81
+ @maxlength = 10
82
+ @lock = Mutex.new
83
+ end
84
+
85
+ def get_connection(ap, handler)
86
+ ret = @pool[ap].shift if @pool.key?(ap) && @pool[ap].length > 0
87
+ ret = create_connection(ap, handler) if ret == nil
88
+ ret
89
+ end
90
+
91
+ def return_connection(con)
92
+ return unless con.connected
93
+ if @pool.key?(con.ap) && @pool[con.ap].length > 0
94
+ if @pool[con.ap].length > @maxlength
95
+ con.close_connection
96
+ else
97
+ @pool[con.ap] << con
98
+ end
99
+ else
100
+ @pool[con.ap] = [con]
101
+ end
102
+ end
103
+
104
+ def create_connection(ap, handler)
105
+ addr,port = ap.split('_')
106
+ con = EventMachine::connect(addr, port, handler)
107
+ con.ap = ap
108
+ con
109
+ end
110
+
111
+ def close_all
112
+ @pool.each_key{|ap| close_at(ap) }
113
+ end
114
+
115
+ def close_at(ap)
116
+ return unless @pool.key?(ap)
117
+ @lock.synchronize {
118
+ while(@pool[ap].length > 0)
119
+ begin
120
+ @pool[ap].shift.close_connection
121
+ rescue =>e
122
+ $log.error("#{e} #{$@}")
123
+ end
124
+ end
125
+ @pool.delete(ap)
126
+ }
127
+ end
128
+ end # class Conpool
129
+
130
+ class Daemon
131
+ attr_reader :daemon
132
+
133
+ @@rttable = nil
134
+
135
+ def self.rttable
136
+ @@rttable
137
+ end
138
+
139
+ def initialize(argv = nil)
140
+ options(argv)
141
+ initialize_logger
142
+ @sender = Roma::Client::Sender.new
143
+ update_rttable(@init_nodes)
144
+ end
145
+
146
+ def initialize_logger
147
+ Roma::Logging::RLogger.create_singleton_instance(@log_path,
148
+ @log_age,
149
+ @log_size)
150
+ end
151
+
152
+ def start
153
+
154
+ timer
155
+
156
+ loop do
157
+ begin
158
+ EventMachine::run do
159
+ EventMachine.start_server('0.0.0.0', @port, ClientHandler)
160
+ EventMachine.start_unix_domain_server("/tmp/#{@uds_name}", ClientHandler)
161
+ end
162
+ rescue =>e
163
+ $log.error("#{e} #{$@}")
164
+ retry
165
+ end
166
+ end
167
+ end
168
+
169
+ private
170
+
171
+ def update_rttable(nodes)
172
+ raise RuntimeError.new("nodes must not be nil.") unless nodes
173
+
174
+ nodes.each { |node|
175
+ rt = make_rttable(node)
176
+ if rt != nil
177
+ @@rttable = rt
178
+ return
179
+ end
180
+ }
181
+ raise RuntimeError.new("fatal error")
182
+ end
183
+
184
+ def make_rttable(node)
185
+ mklhash = @sender.send_route_mklhash_command(node)
186
+ return nil unless mklhash
187
+
188
+ if @@rttable && @@rttable.mklhash == mklhash
189
+ return @@rttable
190
+ end
191
+
192
+ rd = @sender.send_routedump_command(node)
193
+ if rd
194
+ ret = Roma::Client::ClientRoutingTable.new(rd)
195
+ ret.mklhash = mklhash
196
+ return ret
197
+ end
198
+ nil
199
+ rescue =>e
200
+ $log.error("#{e} #{$@}")
201
+ nil
202
+ end
203
+
204
+ def options(argv)
205
+ opts = OptionParser.new
206
+ opts.banner="usage:#{File.basename($0)} [options] addr_port"
207
+
208
+ @uds_name = 'roma'
209
+ opts.on("-n", "--name [name]","Unix domain socket name.default=roma") { |v| @uds_name = v }
210
+ @port = 12345
211
+ opts.on("-p", "--port [port number]","default=12345"){ |v| @port = v.to_i }
212
+ @log_path = "./rcdaemon.log"
213
+ opts.on("-l", "--log [path]","default=./"){ |v|
214
+ @log_path = v
215
+ @log_path << "/" if @log_path[-1] != "/"
216
+ @log_path << "rcdaemon.log"
217
+ }
218
+
219
+ @daemon = true
220
+ opts.on(nil, "--debug"){ @daemon = false }
221
+ @log_age = 10
222
+ @log_size = 1024 * 1024
223
+
224
+ opts.parse!(argv)
225
+ raise OptionParser::ParseError.new if argv.length < 1
226
+ @init_nodes = argv
227
+ rescue OptionParser::ParseError => e
228
+ $stderr.puts e.message
229
+ $stderr.puts opts.help
230
+ exit 1
231
+ end
232
+
233
+ def timer
234
+ Thread.new do
235
+ loop do
236
+ sleep 10
237
+ timer_event_10sec
238
+ end
239
+ end
240
+ end
241
+
242
+ def timer_event_10sec
243
+ update_rttable(@@rttable.nodes)
244
+ end
245
+
246
+ def self.daemon
247
+ p = Process.fork {
248
+ pid=Process.setsid
249
+ Signal.trap(:INT){
250
+ exit! 0
251
+ }
252
+ Signal.trap(:TERM){
253
+ exit! 0
254
+ }
255
+ Signal.trap(:HUP){
256
+ exit! 0
257
+ }
258
+ File.open("/dev/null","r+"){|f|
259
+ STDIN.reopen f
260
+ STDOUT.reopen f
261
+ STDERR.reopen f
262
+ }
263
+ yield
264
+ }
265
+ $stderr.puts p
266
+ exit! 0
267
+ end
268
+
269
+ end # class Daemon
270
+
271
+ end # module Proxy
272
+ end # module Client
273
+ end # module Roma
274
+
275
+ d = Roma::Client::Proxy::Daemon.new(ARGV)
276
+ $log = Roma::Logging::RLogger.instance
277
+ if d.daemon
278
+ Roma::Client::Proxy::Daemon.daemon{ d.start }
279
+ else
280
+ d.start
281
+ end