roma-client 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
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