ls4 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. data/AUTHORS +1 -0
  2. data/COPYING +661 -0
  3. data/ChangeLog +9 -0
  4. data/NOTICE +8 -0
  5. data/README.rdoc +61 -0
  6. data/bin/ls4-cs +3 -0
  7. data/bin/ls4-ds +3 -0
  8. data/bin/ls4-gw +3 -0
  9. data/bin/ls4-standalone +3 -0
  10. data/bin/ls4cmd +3 -0
  11. data/bin/ls4ctl +3 -0
  12. data/bin/ls4rpc +3 -0
  13. data/bin/ls4stat +3 -0
  14. data/bin/ls4top +3 -0
  15. data/lib/ls4/command/cmd.rb +241 -0
  16. data/lib/ls4/command/cs.rb +190 -0
  17. data/lib/ls4/command/ctl.rb +278 -0
  18. data/lib/ls4/command/ds.rb +335 -0
  19. data/lib/ls4/command/gw.rb +256 -0
  20. data/lib/ls4/command/rpc.rb +172 -0
  21. data/lib/ls4/command/standalone.rb +318 -0
  22. data/lib/ls4/command/stat.rb +244 -0
  23. data/lib/ls4/command/top.rb +291 -0
  24. data/lib/ls4/default.rb +26 -0
  25. data/lib/ls4/lib/cclog.rb +220 -0
  26. data/lib/ls4/lib/ebus.rb +553 -0
  27. data/lib/ls4/lib/vbcode.rb +228 -0
  28. data/lib/ls4/logic/fault_detector.rb +212 -0
  29. data/lib/ls4/logic/membership.rb +253 -0
  30. data/lib/ls4/logic/node.rb +66 -0
  31. data/lib/ls4/logic/okey.rb +45 -0
  32. data/lib/ls4/logic/tsv_data.rb +81 -0
  33. data/lib/ls4/logic/weight.rb +166 -0
  34. data/lib/ls4/service/balance.rb +62 -0
  35. data/lib/ls4/service/base.rb +29 -0
  36. data/lib/ls4/service/bus.rb +37 -0
  37. data/lib/ls4/service/config.rb +63 -0
  38. data/lib/ls4/service/config_cs.rb +33 -0
  39. data/lib/ls4/service/config_ds.rb +56 -0
  40. data/lib/ls4/service/config_gw.rb +42 -0
  41. data/lib/ls4/service/data_client.rb +122 -0
  42. data/lib/ls4/service/data_server.rb +168 -0
  43. data/lib/ls4/service/data_server_url.rb +83 -0
  44. data/lib/ls4/service/gateway.rb +375 -0
  45. data/lib/ls4/service/gateway_ro.rb +91 -0
  46. data/lib/ls4/service/gw_http.rb +821 -0
  47. data/lib/ls4/service/heartbeat.rb +182 -0
  48. data/lib/ls4/service/log.rb +81 -0
  49. data/lib/ls4/service/master_select.rb +148 -0
  50. data/lib/ls4/service/mds.rb +292 -0
  51. data/lib/ls4/service/mds_cache.rb +294 -0
  52. data/lib/ls4/service/mds_cache_mem.rb +63 -0
  53. data/lib/ls4/service/mds_cache_memcached.rb +65 -0
  54. data/lib/ls4/service/mds_ha.rb +176 -0
  55. data/lib/ls4/service/mds_memcache.rb +209 -0
  56. data/lib/ls4/service/mds_tc.rb +508 -0
  57. data/lib/ls4/service/mds_tt.rb +472 -0
  58. data/lib/ls4/service/membership.rb +331 -0
  59. data/lib/ls4/service/process.rb +90 -0
  60. data/lib/ls4/service/rpc.rb +50 -0
  61. data/lib/ls4/service/rpc_cs.rb +101 -0
  62. data/lib/ls4/service/rpc_ds.rb +96 -0
  63. data/lib/ls4/service/rpc_gw.rb +255 -0
  64. data/lib/ls4/service/rts.rb +94 -0
  65. data/lib/ls4/service/rts_file.rb +76 -0
  66. data/lib/ls4/service/rts_memory.rb +55 -0
  67. data/lib/ls4/service/slave.rb +132 -0
  68. data/lib/ls4/service/stat.rb +91 -0
  69. data/lib/ls4/service/stat_cs.rb +25 -0
  70. data/lib/ls4/service/stat_ds.rb +40 -0
  71. data/lib/ls4/service/stat_gw.rb +25 -0
  72. data/lib/ls4/service/storage.rb +116 -0
  73. data/lib/ls4/service/storage_dir.rb +201 -0
  74. data/lib/ls4/service/sync.rb +206 -0
  75. data/lib/ls4/service/time_check.rb +80 -0
  76. data/lib/ls4/service/ulog.rb +159 -0
  77. data/lib/ls4/service/ulog_file.rb +398 -0
  78. data/lib/ls4/service/ulog_memory.rb +53 -0
  79. data/lib/ls4/service/weight.rb +134 -0
  80. data/lib/ls4/version.rb +5 -0
  81. data/test/01_add_get_remove.rt +84 -0
  82. data/test/02_read.rt +61 -0
  83. data/test/03_getd_readd.rt +69 -0
  84. data/test/04_version_time.rt +170 -0
  85. data/test/05_version_name.rt +161 -0
  86. data/test/06_http_get_set_remove_1.rt +119 -0
  87. data/test/07_http_get_set_remove_2.rt +116 -0
  88. data/test/08_read_only_time.rt +177 -0
  89. data/test/09_read_only_name.rt +173 -0
  90. data/test/10_http_get_set_remove_3.rt +73 -0
  91. data/test/11_mds_cache_memcached.rt +88 -0
  92. data/test/12_mds_cache_local_memory.rt +86 -0
  93. data/test/13_memcache_mds.rt +84 -0
  94. data/test/14_delete.rt +63 -0
  95. data/test/15_standalone.rt +71 -0
  96. data/test/chukan.rb +516 -0
  97. data/test/common.rb +250 -0
  98. data/test/load_test.rb +79 -0
  99. data/test/load_test_offload.rb +86 -0
  100. 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