ls4 0.9.0
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/AUTHORS +1 -0
- data/COPYING +661 -0
- data/ChangeLog +9 -0
- data/NOTICE +8 -0
- data/README.rdoc +61 -0
- data/bin/ls4-cs +3 -0
- data/bin/ls4-ds +3 -0
- data/bin/ls4-gw +3 -0
- data/bin/ls4-standalone +3 -0
- data/bin/ls4cmd +3 -0
- data/bin/ls4ctl +3 -0
- data/bin/ls4rpc +3 -0
- data/bin/ls4stat +3 -0
- data/bin/ls4top +3 -0
- data/lib/ls4/command/cmd.rb +241 -0
- data/lib/ls4/command/cs.rb +190 -0
- data/lib/ls4/command/ctl.rb +278 -0
- data/lib/ls4/command/ds.rb +335 -0
- data/lib/ls4/command/gw.rb +256 -0
- data/lib/ls4/command/rpc.rb +172 -0
- data/lib/ls4/command/standalone.rb +318 -0
- data/lib/ls4/command/stat.rb +244 -0
- data/lib/ls4/command/top.rb +291 -0
- data/lib/ls4/default.rb +26 -0
- data/lib/ls4/lib/cclog.rb +220 -0
- data/lib/ls4/lib/ebus.rb +553 -0
- data/lib/ls4/lib/vbcode.rb +228 -0
- data/lib/ls4/logic/fault_detector.rb +212 -0
- data/lib/ls4/logic/membership.rb +253 -0
- data/lib/ls4/logic/node.rb +66 -0
- data/lib/ls4/logic/okey.rb +45 -0
- data/lib/ls4/logic/tsv_data.rb +81 -0
- data/lib/ls4/logic/weight.rb +166 -0
- data/lib/ls4/service/balance.rb +62 -0
- data/lib/ls4/service/base.rb +29 -0
- data/lib/ls4/service/bus.rb +37 -0
- data/lib/ls4/service/config.rb +63 -0
- data/lib/ls4/service/config_cs.rb +33 -0
- data/lib/ls4/service/config_ds.rb +56 -0
- data/lib/ls4/service/config_gw.rb +42 -0
- data/lib/ls4/service/data_client.rb +122 -0
- data/lib/ls4/service/data_server.rb +168 -0
- data/lib/ls4/service/data_server_url.rb +83 -0
- data/lib/ls4/service/gateway.rb +375 -0
- data/lib/ls4/service/gateway_ro.rb +91 -0
- data/lib/ls4/service/gw_http.rb +821 -0
- data/lib/ls4/service/heartbeat.rb +182 -0
- data/lib/ls4/service/log.rb +81 -0
- data/lib/ls4/service/master_select.rb +148 -0
- data/lib/ls4/service/mds.rb +292 -0
- data/lib/ls4/service/mds_cache.rb +294 -0
- data/lib/ls4/service/mds_cache_mem.rb +63 -0
- data/lib/ls4/service/mds_cache_memcached.rb +65 -0
- data/lib/ls4/service/mds_ha.rb +176 -0
- data/lib/ls4/service/mds_memcache.rb +209 -0
- data/lib/ls4/service/mds_tc.rb +508 -0
- data/lib/ls4/service/mds_tt.rb +472 -0
- data/lib/ls4/service/membership.rb +331 -0
- data/lib/ls4/service/process.rb +90 -0
- data/lib/ls4/service/rpc.rb +50 -0
- data/lib/ls4/service/rpc_cs.rb +101 -0
- data/lib/ls4/service/rpc_ds.rb +96 -0
- data/lib/ls4/service/rpc_gw.rb +255 -0
- data/lib/ls4/service/rts.rb +94 -0
- data/lib/ls4/service/rts_file.rb +76 -0
- data/lib/ls4/service/rts_memory.rb +55 -0
- data/lib/ls4/service/slave.rb +132 -0
- data/lib/ls4/service/stat.rb +91 -0
- data/lib/ls4/service/stat_cs.rb +25 -0
- data/lib/ls4/service/stat_ds.rb +40 -0
- data/lib/ls4/service/stat_gw.rb +25 -0
- data/lib/ls4/service/storage.rb +116 -0
- data/lib/ls4/service/storage_dir.rb +201 -0
- data/lib/ls4/service/sync.rb +206 -0
- data/lib/ls4/service/time_check.rb +80 -0
- data/lib/ls4/service/ulog.rb +159 -0
- data/lib/ls4/service/ulog_file.rb +398 -0
- data/lib/ls4/service/ulog_memory.rb +53 -0
- data/lib/ls4/service/weight.rb +134 -0
- data/lib/ls4/version.rb +5 -0
- data/test/01_add_get_remove.rt +84 -0
- data/test/02_read.rt +61 -0
- data/test/03_getd_readd.rt +69 -0
- data/test/04_version_time.rt +170 -0
- data/test/05_version_name.rt +161 -0
- data/test/06_http_get_set_remove_1.rt +119 -0
- data/test/07_http_get_set_remove_2.rt +116 -0
- data/test/08_read_only_time.rt +177 -0
- data/test/09_read_only_name.rt +173 -0
- data/test/10_http_get_set_remove_3.rt +73 -0
- data/test/11_mds_cache_memcached.rt +88 -0
- data/test/12_mds_cache_local_memory.rt +86 -0
- data/test/13_memcache_mds.rt +84 -0
- data/test/14_delete.rt +63 -0
- data/test/15_standalone.rt +71 -0
- data/test/chukan.rb +516 -0
- data/test/common.rb +250 -0
- data/test/load_test.rb +79 -0
- data/test/load_test_offload.rb +86 -0
- metadata +295 -0
@@ -0,0 +1,294 @@
|
|
1
|
+
#
|
2
|
+
# LS4
|
3
|
+
# Copyright (C) 2010-2011 FURUHASHI Sadayuki
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU Affero General Public License as
|
7
|
+
# published by the Free Software Foundation, either version 3 of the
|
8
|
+
# License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU Affero General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Affero General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
module LS4
|
19
|
+
|
20
|
+
|
21
|
+
class MDSCacheBus < Bus
|
22
|
+
call_slot :get
|
23
|
+
call_slot :set
|
24
|
+
call_slot :invalidate
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
class MDSCacheConfigService < Service
|
29
|
+
def run
|
30
|
+
@uri = ConfigBus.get_initial_mds_cache_uri
|
31
|
+
@uri ||= "null"
|
32
|
+
on_change
|
33
|
+
end
|
34
|
+
|
35
|
+
def rpc_get_mds_cache_uri
|
36
|
+
@uri
|
37
|
+
end
|
38
|
+
|
39
|
+
def rpc_set_mds_cache_uri(uri)
|
40
|
+
@uri = uri
|
41
|
+
on_change
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.hash_uri(uri)
|
46
|
+
Digest::SHA1.digest(uri)
|
47
|
+
end
|
48
|
+
|
49
|
+
def on_change
|
50
|
+
SyncBus.update(SYNC_MDS_CACHE_URI,
|
51
|
+
@uri, MDSCacheConfigService.hash_uri(@uri))
|
52
|
+
end
|
53
|
+
|
54
|
+
ebus_connect :ProcessBus,
|
55
|
+
:run
|
56
|
+
|
57
|
+
ebus_connect :CSRPCBus,
|
58
|
+
:get_mds_cache_uri => :rpc_get_mds_cache_uri,
|
59
|
+
:set_mds_cache_uri => :rpc_set_mds_cache_uri
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
class MDSCacheService < Service
|
64
|
+
def initialize
|
65
|
+
@uri = ""
|
66
|
+
@cache = NullMDSCache.new
|
67
|
+
end
|
68
|
+
|
69
|
+
def run
|
70
|
+
SyncBus.register_callback(SYNC_MDS_CACHE_URI,
|
71
|
+
MDSCacheConfigService.hash_uri(@uri)) do |obj|
|
72
|
+
uri = obj
|
73
|
+
reopen(uri)
|
74
|
+
@uri = uri
|
75
|
+
MDSCacheConfigService.hash_uri(@uri)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def shutdown
|
80
|
+
if @cache
|
81
|
+
@cache.close rescue nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def reopen(uri)
|
86
|
+
klass, expr = MDSCacheSelector.select_class(uri)
|
87
|
+
|
88
|
+
cache = klass.new
|
89
|
+
cache.open(expr)
|
90
|
+
|
91
|
+
old_cache = @cache
|
92
|
+
@cache = cache
|
93
|
+
|
94
|
+
$log.info "using MDS cache: #{@cache}"
|
95
|
+
|
96
|
+
begin
|
97
|
+
old_cache.close
|
98
|
+
rescue
|
99
|
+
$log.error "MDSCache close error: #{$!}"
|
100
|
+
$log.error_backtrace $!.backtrace
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
ebus_connect :ProcessBus,
|
105
|
+
:run,
|
106
|
+
:shutdown
|
107
|
+
|
108
|
+
ebus_connect :MDSCacheBus,
|
109
|
+
:get,
|
110
|
+
:set,
|
111
|
+
:invalidate
|
112
|
+
|
113
|
+
extend Forwardable
|
114
|
+
|
115
|
+
def_delegators :@cache,
|
116
|
+
:get,
|
117
|
+
:set,
|
118
|
+
:invalidate
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
module MDSCacheSelector
|
123
|
+
IMPLS = {}
|
124
|
+
|
125
|
+
def self.register(name, klass)
|
126
|
+
IMPLS[name.to_sym] = klass
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.select_class(uri)
|
131
|
+
if uri.empty?
|
132
|
+
return NullMDSCache
|
133
|
+
end
|
134
|
+
|
135
|
+
if m = /^(\w{1,8})\:(.*)/.match(uri)
|
136
|
+
type = m[1].to_sym
|
137
|
+
expr = m[2]
|
138
|
+
else
|
139
|
+
type = :null
|
140
|
+
expr = uri
|
141
|
+
end
|
142
|
+
|
143
|
+
klass = IMPLS[type]
|
144
|
+
unless klass
|
145
|
+
raise "unknown MDSCache type: #{type}"
|
146
|
+
end
|
147
|
+
|
148
|
+
return klass, expr
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
|
153
|
+
class MDSCache
|
154
|
+
#def open(expr)
|
155
|
+
#end
|
156
|
+
|
157
|
+
#def close
|
158
|
+
#end
|
159
|
+
|
160
|
+
#def get(key)
|
161
|
+
#end
|
162
|
+
|
163
|
+
#def set(key, val)
|
164
|
+
#end
|
165
|
+
|
166
|
+
#def invalidate(key)
|
167
|
+
#end
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
class NullMDSCache < MDSCache
|
172
|
+
MDSCacheSelector.register(:null, self)
|
173
|
+
|
174
|
+
def open(expr)
|
175
|
+
end
|
176
|
+
|
177
|
+
def close
|
178
|
+
end
|
179
|
+
|
180
|
+
def get(key)
|
181
|
+
nil
|
182
|
+
end
|
183
|
+
|
184
|
+
def set(key, val)
|
185
|
+
end
|
186
|
+
|
187
|
+
def invalidate(key)
|
188
|
+
end
|
189
|
+
|
190
|
+
def to_s
|
191
|
+
"no-cache"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
class CachedMDSBus < Bus
|
197
|
+
call_slot :get_okey
|
198
|
+
call_slot :get_attrs
|
199
|
+
call_slot :get_okey_attrs
|
200
|
+
call_slot :add
|
201
|
+
call_slot :update_attrs
|
202
|
+
call_slot :remove
|
203
|
+
call_slot :delete
|
204
|
+
call_slot :util_locate
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
class CachedMDSService < Service
|
209
|
+
def get_okey(key, version=nil, &cb)
|
210
|
+
if version == nil
|
211
|
+
if okey = get_cache(key)
|
212
|
+
cb.call(okey, nil)
|
213
|
+
return
|
214
|
+
end
|
215
|
+
end
|
216
|
+
MDSBus.get_okey(key, version) {|okey,error|
|
217
|
+
set_cache(key, okey) if okey
|
218
|
+
cb.call(okey, error)
|
219
|
+
}
|
220
|
+
end
|
221
|
+
|
222
|
+
def get_attrs(key, version=nil, &cb)
|
223
|
+
MDSBus.get_attrs(key, version, &cb)
|
224
|
+
end
|
225
|
+
|
226
|
+
def get_okey_attrs(key, version=nil, &cb)
|
227
|
+
MDSBus.get_okey_attrs(key, version, &cb)
|
228
|
+
end
|
229
|
+
|
230
|
+
def add(key, attrs={}, vname=nil, &cb)
|
231
|
+
invalidate_cache(key)
|
232
|
+
MDSBus.add(key, attrs, vname, &cb)
|
233
|
+
end
|
234
|
+
|
235
|
+
def update_attrs(key, attrs, &cb)
|
236
|
+
invalidate_cache(key)
|
237
|
+
MDSBus.update_attrs(key, attrs, &cb)
|
238
|
+
end
|
239
|
+
|
240
|
+
def remove(key, &cb)
|
241
|
+
invalidate_cache(key)
|
242
|
+
MDSBus.remove(key, &cb)
|
243
|
+
end
|
244
|
+
|
245
|
+
def delete(key, version=nil, &cb)
|
246
|
+
invalidate_cache(key)
|
247
|
+
MDSBus.delete(key, version, &cb)
|
248
|
+
end
|
249
|
+
|
250
|
+
def util_locate(key, &cb)
|
251
|
+
MDSBus.util_locate(key, &cb)
|
252
|
+
end
|
253
|
+
|
254
|
+
ebus_connect :CachedMDSBus,
|
255
|
+
:get_okey,
|
256
|
+
:get_attrs,
|
257
|
+
:get_okey_attrs,
|
258
|
+
:add,
|
259
|
+
:update_attrs,
|
260
|
+
:remove,
|
261
|
+
:delete,
|
262
|
+
:util_locate
|
263
|
+
|
264
|
+
private
|
265
|
+
def get_cache(key)
|
266
|
+
if val = MDSCacheBus.get(key)
|
267
|
+
rsid, vtime = MessagePack.unpack(val)
|
268
|
+
return ObjectKey.new(key, vtime, rsid)
|
269
|
+
end
|
270
|
+
return nil
|
271
|
+
rescue
|
272
|
+
$log.warn("error when getting MDS cache: key=#{key.inspect}: #{$!.to_s}")
|
273
|
+
$log.debug_backtrace $!.backtrace
|
274
|
+
return nil
|
275
|
+
end
|
276
|
+
|
277
|
+
def set_cache(key, okey)
|
278
|
+
val = [okey.rsid, okey.vtime].to_msgpack
|
279
|
+
MDSCacheBus.set(key, val)
|
280
|
+
rescue
|
281
|
+
$log.warn("error when setting MDS cache: key=#{key.inspect}: #{$!.to_s}")
|
282
|
+
$log.debug_backtrace $!.backtrace
|
283
|
+
end
|
284
|
+
|
285
|
+
def invalidate_cache(key)
|
286
|
+
MDSCacheBus.invalidate(key)
|
287
|
+
rescue
|
288
|
+
$log.warn("error when invalidating MDS cache: key=#{key.inspect}: #{$!.to_s}")
|
289
|
+
$log.debug_backtrace $!.backtrace
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
|
294
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#
|
2
|
+
# LS4
|
3
|
+
# Copyright (C) 2010-2011 FURUHASHI Sadayuki
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU Affero General Public License as
|
7
|
+
# published by the Free Software Foundation, either version 3 of the
|
8
|
+
# License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU Affero General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Affero General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
module LS4
|
19
|
+
|
20
|
+
|
21
|
+
class LocalMemoryMDSCache < MDSCache
|
22
|
+
MDSCacheSelector.register(:local, self)
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
require 'tokyocabinet'
|
26
|
+
end
|
27
|
+
|
28
|
+
def open(expr)
|
29
|
+
@db = TokyoCabinet::ADB.new
|
30
|
+
if expr.empty?
|
31
|
+
@size = "32m"
|
32
|
+
else
|
33
|
+
@size = expr
|
34
|
+
end
|
35
|
+
name = "+#capsiz=#{@size}"
|
36
|
+
unless @db.open(name)
|
37
|
+
raise "failed to MDS local memory cache database: #{@db.errmsg(@db.ecode)}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def close
|
42
|
+
@db.close
|
43
|
+
end
|
44
|
+
|
45
|
+
def get(key)
|
46
|
+
@db[key]
|
47
|
+
end
|
48
|
+
|
49
|
+
def set(key, val)
|
50
|
+
@db[key] = val
|
51
|
+
end
|
52
|
+
|
53
|
+
def invalidate(key)
|
54
|
+
@db.delete(key)
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_s
|
58
|
+
"<LocalMemoryMDSCache size=#{@size}>"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
#
|
2
|
+
# LS4
|
3
|
+
# Copyright (C) 2010-2011 FURUHASHI Sadayuki
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU Affero General Public License as
|
7
|
+
# published by the Free Software Foundation, either version 3 of the
|
8
|
+
# License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU Affero General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Affero General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
module LS4
|
19
|
+
|
20
|
+
|
21
|
+
class MemcachedMDSCache < MDSCache
|
22
|
+
MDSCacheSelector.register(:mc, self)
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
require 'memcache'
|
26
|
+
end
|
27
|
+
|
28
|
+
def open(expr)
|
29
|
+
@expire = 0
|
30
|
+
if m = /\;expire\=(\d+)/.match(expr)
|
31
|
+
servers_line = expr[0,m.begin(0)]
|
32
|
+
@expire = m[1].to_i
|
33
|
+
else
|
34
|
+
servers_line = expr
|
35
|
+
end
|
36
|
+
@servers = servers_line.split(/\s*\,\s*/)
|
37
|
+
if @expire == 0
|
38
|
+
@expire = 60*60*24
|
39
|
+
end
|
40
|
+
@mc = MemCache.new(@servers, {:urlencode => false, :compression => false, :multithread => true, :timeout => 1.0})
|
41
|
+
end
|
42
|
+
|
43
|
+
def close
|
44
|
+
@mc.reset
|
45
|
+
end
|
46
|
+
|
47
|
+
def get(key)
|
48
|
+
@mc.get(key, true)
|
49
|
+
end
|
50
|
+
|
51
|
+
def set(key, val)
|
52
|
+
@mc.set(key, val, @expire, true)
|
53
|
+
end
|
54
|
+
|
55
|
+
def invalidate(key)
|
56
|
+
@mc.delete(key)
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_s
|
60
|
+
"<MemcachedMDSCache servers=#{@servers.join(',')} expire=#{@expire}>"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
#
|
2
|
+
# LS4
|
3
|
+
# Copyright (C) 2010-2011 FURUHASHI Sadayuki
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU Affero General Public License as
|
7
|
+
# published by the Free Software Foundation, either version 3 of the
|
8
|
+
# License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU Affero General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Affero General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
|
19
|
+
module LS4
|
20
|
+
|
21
|
+
|
22
|
+
# single node:
|
23
|
+
# host1:port1
|
24
|
+
#
|
25
|
+
# master-slave:
|
26
|
+
# host1:port1,host2:port2
|
27
|
+
#
|
28
|
+
# master-slave with read weight:
|
29
|
+
# host1:port1,host2:port2;0,1
|
30
|
+
#
|
31
|
+
# dual-master:
|
32
|
+
# host1:port1--host2:port2
|
33
|
+
#
|
34
|
+
class BasicHADB
|
35
|
+
DEFAULT_WEIGHT = 10
|
36
|
+
|
37
|
+
def initialize(expr)
|
38
|
+
@dbmap = {} # {Address => DB}
|
39
|
+
@writers = [] # [Address]
|
40
|
+
@readers = [] # [Address]
|
41
|
+
@readers_rr = 0
|
42
|
+
|
43
|
+
expr.split('--').each {|line|
|
44
|
+
nodes, weights = line.strip.split(';',2)
|
45
|
+
|
46
|
+
addrs = nodes.strip.split(',').map {|addr|
|
47
|
+
parse_addr(addr)
|
48
|
+
}
|
49
|
+
|
50
|
+
weights = (weights||"").strip.split(',').map {|x| x.to_i }
|
51
|
+
|
52
|
+
@writers << addrs.first
|
53
|
+
|
54
|
+
addrs.each_with_index {|addr,i|
|
55
|
+
weight = weights[i] ||= DEFAULT_WEIGHT
|
56
|
+
weight.times {
|
57
|
+
@readers << addr
|
58
|
+
}
|
59
|
+
@dbmap[addr] = nil
|
60
|
+
}
|
61
|
+
|
62
|
+
$log.info "MDS -- #{addrs.join(',')};#{weights.join(',')}"
|
63
|
+
}
|
64
|
+
|
65
|
+
if @dbmap.empty?
|
66
|
+
raise "empty expression"
|
67
|
+
end
|
68
|
+
|
69
|
+
if @dbmap.size == 1
|
70
|
+
# single node
|
71
|
+
@readers = [@readers[0]]
|
72
|
+
else
|
73
|
+
@readers = @readers.sort_by {|addr| rand }
|
74
|
+
end
|
75
|
+
|
76
|
+
# open remote database
|
77
|
+
@dbmap.keys.each {|addr|
|
78
|
+
@dbmap[addr] = open_db(addr)
|
79
|
+
}
|
80
|
+
|
81
|
+
rescue
|
82
|
+
@dbmap.each_pair {|addr,db|
|
83
|
+
if db
|
84
|
+
close_db(db) rescue nil
|
85
|
+
end
|
86
|
+
}
|
87
|
+
$log.error $!
|
88
|
+
$log.error_backtrace $!.backtrace
|
89
|
+
raise "MDS: invlaid address expression: #{$!}"
|
90
|
+
end
|
91
|
+
|
92
|
+
def write(shard_key, &block)
|
93
|
+
if @writers.size == 1
|
94
|
+
n = 0
|
95
|
+
else
|
96
|
+
n = hash_key(shard_key) % @writers.size
|
97
|
+
end
|
98
|
+
ha_call(@writers, n) {|db|
|
99
|
+
block.call(db)
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
def read(shard_key, &block)
|
104
|
+
@readers_rr += 1
|
105
|
+
@readers_rr = 0 if @readers_rr >= @readers.size
|
106
|
+
ha_call(@readers, @readers_rr) {|db|
|
107
|
+
block.call(db)
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
def close
|
112
|
+
@dbmap.each_pair {|addr,db|
|
113
|
+
close_db(db) rescue nil
|
114
|
+
}
|
115
|
+
end
|
116
|
+
|
117
|
+
protected
|
118
|
+
def parse_addr(addr)
|
119
|
+
host, port = addr.split(':',2)
|
120
|
+
port ||= self.class::DEFAULT_PORT
|
121
|
+
port = port.to_i
|
122
|
+
host.strip!
|
123
|
+
Address.new(host, port)
|
124
|
+
end
|
125
|
+
|
126
|
+
def open_db(addr)
|
127
|
+
raise "LOGIC ERROR: not implemented!"
|
128
|
+
end
|
129
|
+
|
130
|
+
def ensure_db(db, addr)
|
131
|
+
true
|
132
|
+
end
|
133
|
+
|
134
|
+
def error_result?(db, result)
|
135
|
+
nil
|
136
|
+
end
|
137
|
+
|
138
|
+
def close_db(db)
|
139
|
+
db.close
|
140
|
+
end
|
141
|
+
|
142
|
+
def hash_key(key)
|
143
|
+
digest = Digest::MD5.digest(key)
|
144
|
+
digest.unpack('C')[0]
|
145
|
+
end
|
146
|
+
|
147
|
+
def ha_call(array, idx, &block)
|
148
|
+
db = nil
|
149
|
+
failed = []
|
150
|
+
sz = array.size
|
151
|
+
sz.times {
|
152
|
+
addr = array[idx % sz]
|
153
|
+
unless failed.include?(addr)
|
154
|
+
db = @dbmap[addr]
|
155
|
+
if ensure_db(db, addr)
|
156
|
+
@dbmap[addr] = db # FIXME
|
157
|
+
begin
|
158
|
+
result = block.call(db)
|
159
|
+
if err = error_result?(db, result)
|
160
|
+
failed << [addr, err]
|
161
|
+
else
|
162
|
+
return result
|
163
|
+
end
|
164
|
+
rescue
|
165
|
+
failed << [addr, $!]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
idx += 1
|
170
|
+
}
|
171
|
+
raise "MDS error: #{failed.inspect}" # TODO error message
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
end
|