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,80 @@
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 TimeCheckService < Service
22
+ THRESHOLD = 3
23
+ INTERVAL = 60*10
24
+
25
+ def check_blocking!
26
+ do_check.join
27
+ nil
28
+ end
29
+
30
+ def run
31
+ @timer = ProcessBus.start_timer(INTERVAL, true) do
32
+ on_timer
33
+ end
34
+ end
35
+
36
+ def shutdown
37
+ # FIXME stop @timer
38
+ @timer = nil
39
+ end
40
+
41
+ def on_timer
42
+ do_check if @timer
43
+ end
44
+
45
+ ebus_connect :ProcessBus,
46
+ :run,
47
+ :shutdown
48
+
49
+ private
50
+ def do_check
51
+ get_cs_session.callback(:stat, 'time') do |future|
52
+ begin
53
+ cs_time = future.get
54
+ ack_check(cs_time)
55
+ rescue
56
+ $log.error "time check error: #{$!}"
57
+ $log.error_backtrace $!.backtrace
58
+ end
59
+ end
60
+ end
61
+
62
+ def ack_check(cs_time)
63
+ my_time = StatService.instance.stat_time
64
+ diff = cs_time - my_time
65
+ diff_abs = diff > 0 ? diff : -diff
66
+ if diff_abs >= THRESHOLD
67
+ cs_utc = Time.at(cs_time).utc
68
+ my_utc = Time.at(my_time).utc
69
+ $log.warn "sytem time must be adjusted: cs=[#{cs_utc}] me=[#{my_utc}]"
70
+ end
71
+ end
72
+
73
+ private
74
+ def get_cs_session
75
+ ProcessBus.get_session(ConfigBus.get_cs_address)
76
+ end
77
+ end
78
+
79
+
80
+ end
@@ -0,0 +1,159 @@
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 UpdateLogBus < Bus
22
+ call_slot :open
23
+ call_slot :close
24
+
25
+ call_slot :append
26
+
27
+ call_slot :get
28
+ end
29
+
30
+
31
+ class UpdateLogSelector
32
+ IMPLS = {}
33
+
34
+ def self.register(name, klass)
35
+ IMPLS[name.to_sym] = klass
36
+ nil
37
+ end
38
+
39
+ def self.select_class(uri)
40
+ uri ||= "mem:"
41
+
42
+ if m = /^(\w{1,8})\:(.*)/.match(uri)
43
+ type = m[1].to_sym
44
+ expr = m[2]
45
+ else
46
+ type = :file
47
+ expr = uri
48
+ end
49
+
50
+ klass = IMPLS[type]
51
+
52
+ unless klass
53
+ "unknown UpdateLog type: #{type}"
54
+ end
55
+
56
+ return klass, expr
57
+ end
58
+
59
+ def self.select!(uri)
60
+ klass, expr = select_class(uri)
61
+ klass.init
62
+
63
+ UpdateLogBus.open(expr)
64
+ end
65
+
66
+ def self.open!
67
+ select!(ConfigBus.get_ulog_path)
68
+ end
69
+ end
70
+
71
+
72
+ class UpdateLogService < Service
73
+ #def open(expr)
74
+ #end
75
+
76
+ #def close
77
+ #end
78
+
79
+ #def append(data, &block)
80
+ #end
81
+
82
+ #def get(pos)
83
+ #end
84
+
85
+ ebus_connect :UpdateLogBus,
86
+ :append,
87
+ :get,
88
+ :open,
89
+ :close
90
+
91
+ def shutdown
92
+ UpdateLogBus.close
93
+ end
94
+
95
+ ebus_connect :ProcessBus,
96
+ :shutdown
97
+ end
98
+
99
+
100
+ # +-+------+--...---+
101
+ # |1|vbcode| raw |
102
+ # +-+------+--...---+
103
+ # 0x91
104
+ # vtime
105
+ # key
106
+ #
107
+ # +-+------+------+------+--...---+
108
+ # |1|vbcode|vbcode|vbcode| raw |
109
+ # +-+------+------+------+--...---+
110
+ # 0x93
111
+ # vtime
112
+ # offset
113
+ # size
114
+ # key
115
+ #
116
+ class UpdateLogData
117
+ def initialize(vtime, key, *meta)
118
+ @vtime = vtime
119
+ @key = key
120
+ @meta = meta
121
+ end
122
+
123
+ attr_reader :vtime
124
+ attr_reader :key
125
+ attr_reader :meta
126
+
127
+ def offset
128
+ @meta[0]
129
+ end
130
+
131
+ def size
132
+ @meta[1]
133
+ end
134
+
135
+ def dump
136
+ raw = [0x91 + @meta.size].pack('C')
137
+ VariableByteCode.encode(@vtime, raw)
138
+ meta.each {|m|
139
+ VariableByteCode.encode(m, raw)
140
+ }
141
+ raw << key
142
+ raw
143
+ end
144
+
145
+ def self.load(raw)
146
+ n = raw.unpack('C')[0]
147
+ vtime, i = VariableByteCode.decode_index(raw, 1)
148
+ meta = []
149
+ (n - 0x91).times {
150
+ m, i = VariableByteCode.decode_index(raw, i)
151
+ meta << m
152
+ }
153
+ key = raw[i..-1]
154
+ new(vtime, key, *meta)
155
+ end
156
+ end
157
+
158
+
159
+ end
@@ -0,0 +1,398 @@
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 FileUpdateLog < UpdateLogService
22
+ UpdateLogSelector.register(:file, self)
23
+
24
+ # File format:
25
+ # +-------+-------+-------+----...+---
26
+ # | 8 | 8 | 8 | 48 | records...
27
+ # +-------+-------+-------+----...+---
28
+ # magic "SoULog00"
29
+ # start-timestamp
30
+ # pos
31
+ # reserved...
32
+ #
33
+ # Record format:
34
+ # +------+-------+------...+
35
+ # |vbcode|msgpack| raw |
36
+ # +------+-------+------...+
37
+ # record-size
38
+ # metadata
39
+ # data
40
+ # | |
41
+ # |-----------------|
42
+ # record-size
43
+ #
44
+
45
+ HEADER_SIZE = 64
46
+ MAGICK = "SoULog00"
47
+
48
+ class Record
49
+ def initialize(reltime, data)
50
+ @reltime = reltime
51
+ @data = data
52
+ end
53
+
54
+ attr_reader :reltime
55
+ attr_reader :data
56
+
57
+ def write(stream)
58
+ meta = [@reltime]
59
+
60
+ bmeta = meta.to_msgpack
61
+ rsize = bmeta.size + @data.size
62
+ brsize = VariableByteCode.encode(rsize)
63
+
64
+ stream.write(brsize)
65
+ stream.write(bmeta)
66
+ stream.write(@data)
67
+
68
+ brsize.size + bmeta.size + @data.size
69
+ end
70
+
71
+ def self.read(stream)
72
+ new *read_impl(stream, true)
73
+ end
74
+
75
+ def self.read_nodata(stream)
76
+ return read_impl(stream, false)
77
+ end
78
+
79
+ private
80
+ def self.read_impl(stream, need_data)
81
+ ipos = stream.pos
82
+
83
+ hdr = stream.read(32)
84
+ if hdr.nil? || hdr.empty?
85
+ return nil
86
+ end
87
+
88
+ rsize, rsize_len = VariableByteCode.decode_index(hdr)
89
+
90
+ u = MessagePack::Unpacker.new
91
+ meta_len = u.execute(hdr, rsize_len) - rsize_len
92
+ unless u.finished?
93
+ return nil # FIXME
94
+ end
95
+ meta = u.data
96
+
97
+ reltime = meta[0]
98
+
99
+ if need_data
100
+ stream.pos = ipos + rsize_len + meta_len
101
+ data = stream.read(rsize - meta_len)
102
+ return reltime, data
103
+ else
104
+ stream.pos = ipos + rsize_len + rsize
105
+ return reltime
106
+ end
107
+ end
108
+ end
109
+
110
+ class RecordRef
111
+ def initialize(reltime, pos)
112
+ @reltime = reltime
113
+ @pos = pos
114
+ end
115
+
116
+ attr_reader :reltime
117
+ attr_reader :pos
118
+
119
+ def <=>(o)
120
+ @reltime <=> o.reltime
121
+ end
122
+
123
+ def read_body(stream)
124
+ stream.pos = @pos
125
+ Record.read(stream)
126
+ end
127
+
128
+ def self.read(stream)
129
+ pos = stream.pos
130
+ reltime = Record.read_nodata(stream)
131
+ new(reltime, pos)
132
+ end
133
+ end
134
+
135
+ class Header
136
+ def initialize(atime, pos)
137
+ @atime = atime
138
+ @pos = pos
139
+ end
140
+
141
+ attr_reader :atime
142
+ attr_reader :pos
143
+
144
+ def dump
145
+ raw = ""
146
+ raw << MAGICK
147
+ raw << [ atime>>32, atime&0xffffffff].pack('NN')
148
+ raw << [pos>>32, pos&0xffffffff].pack('NN')
149
+ raw << ([0] * (HEADER_SIZE - raw.size)).pack('C*')
150
+ raw
151
+ end
152
+
153
+ def self.load(raw)
154
+ magick = raw[0,8]
155
+ if magick != MAGICK
156
+ raise "magick not match"
157
+ end
158
+
159
+ atime = raw[8,8].unpack('NN')
160
+ atime = (atime[0]<<32) | atime[1]
161
+ pos = raw[16,8].unpack('NN')
162
+ pos = (pos[0]<<32) | pos[1]
163
+
164
+ new(atime, pos)
165
+ end
166
+
167
+ def self.read_offset(stream)
168
+ stream.pos = 16
169
+ raw = stream.read(8)
170
+ pos = raw.unpack('NN')
171
+ pos = (pos[0]<<32) | pos[1]
172
+ pos
173
+ end
174
+
175
+ def self.write_offset(stream, pos)
176
+ stream.pos = 16
177
+ raw = [pos>>32, pos&0xffffffff].pack('NN')
178
+ stream.write(raw)
179
+ stream
180
+ end
181
+ end
182
+
183
+ class LogFile
184
+ def initialize(path, atime)
185
+ @path = path
186
+ @file = File.open(@path, File::RDWR|File::CREAT)
187
+ if @file.stat.size == 0
188
+ init_file(atime)
189
+ else
190
+ read_file
191
+ end
192
+ end
193
+
194
+ def close
195
+ @file.close
196
+ end
197
+
198
+ def append(atime, data, &block)
199
+ reltime = atime - @atime
200
+ if reltime <= @last_reltime
201
+ reltime = @last_reltime+1
202
+ end
203
+ @last_reltime = reltime
204
+
205
+ pos = get_offset
206
+
207
+ r = Record.new(reltime, data)
208
+ ref = RecordRef.new(reltime, pos)
209
+
210
+ @file.pos = pos
211
+ r.write(@file)
212
+ noffset = @file.pos
213
+
214
+ block.call
215
+
216
+ @index << ref
217
+ set_offset(noffset)
218
+
219
+ reltime + @atime
220
+ end
221
+
222
+ def get(time)
223
+ reltime = time - @atime
224
+
225
+ while true
226
+ # FIXME binary search
227
+ ref = @index.find {|ref|
228
+ ref.reltime > reltime
229
+ }
230
+
231
+ unless ref
232
+ break
233
+ end
234
+
235
+ r = ref.read_body(@file)
236
+
237
+ return r.data, r.reltime + @atime
238
+ end
239
+
240
+ return nil, reltime + @atime
241
+ end
242
+
243
+ private
244
+ def init_file(atime)
245
+ header = Header.new(atime, HEADER_SIZE)
246
+
247
+ @file.pos = 0
248
+ @file.write(header.dump)
249
+
250
+ @atime = header.atime
251
+ @index = []
252
+ @last_reltime = 0
253
+ end
254
+
255
+ def read_file
256
+ @file.pos = 0
257
+ raw = @file.read(HEADER_SIZE)
258
+ header = Header.load(raw)
259
+
260
+ @atime = header.atime
261
+ @index = []
262
+ @last_reltime = 0
263
+
264
+ pos = header.pos
265
+
266
+ @file.pos = HEADER_SIZE
267
+ while @file.pos < pos
268
+ r = RecordRef.read(@file)
269
+ break unless r
270
+ @index << r
271
+ @last_reltime = r.reltime
272
+ end
273
+
274
+ noffset = @file.pos
275
+
276
+ set_offset(noffset)
277
+ end
278
+
279
+ def get_offset
280
+ Header.read_offset(@file)
281
+ end
282
+
283
+ def set_offset(pos)
284
+ Header.write_offset(@file, pos)
285
+ pos
286
+ end
287
+ end
288
+
289
+ def open(expr)
290
+ @dir = expr
291
+ atime = next_time
292
+ # FIXME rotation
293
+ @f = LogFile.new("#{@dir}/ulog-0000000001", atime)
294
+ end
295
+
296
+ def close
297
+ @f.close
298
+ end
299
+
300
+ def append(data, &block)
301
+ # FIXME rotation
302
+ atime = next_time
303
+ atime = @f.append(atime, data, &block)
304
+ atime
305
+ end
306
+
307
+ def get(pos)
308
+ # FIXME rotation
309
+ @f.get(pos)
310
+ end
311
+
312
+ private
313
+ def next_time
314
+ # FIXME
315
+ Time.now.to_i
316
+ end
317
+ end
318
+
319
+
320
+ end
321
+
322
+
323
+ if $0 == __FILE__
324
+ require 'msgpack'
325
+ require 'stringio'
326
+ include LS4
327
+
328
+
329
+ puts "testing FileUpdateLog::Record ..."
330
+
331
+ def check(a, b)
332
+ if a.reltime != b.reltime ||
333
+ a.data != b.data
334
+ raise "test failed: expect #{a.inspect} but #{b.inspect}"
335
+ end
336
+ end
337
+
338
+ src = []
339
+ io = StringIO.new
340
+
341
+ 1000.times {|i|
342
+ s = FileUpdateLog::Record.new(rand(i).to_i, "abc"*rand(i*5))
343
+ s.write(io)
344
+ src << s
345
+ }
346
+
347
+ io.pos = 0
348
+
349
+ src.each {|s|
350
+ r = FileUpdateLog::Record.read(io)
351
+ check(s, r)
352
+ }
353
+
354
+ puts "ok"
355
+
356
+
357
+ puts "testing FileUpdateLog ..."
358
+
359
+ ulog = FileUpdateLog.new("./test/")
360
+ begin
361
+
362
+ pos = ulog.append("dummy") do
363
+ true
364
+ end
365
+
366
+ src = []
367
+ 100.times {|i|
368
+ data = "data#{rand(i)}"
369
+
370
+ ulog.append(data) do
371
+ end
372
+
373
+ src << data
374
+ }
375
+
376
+ while true
377
+ data, pos = ulog.get(pos)
378
+ unless data
379
+ break
380
+ end
381
+
382
+ s = src.shift
383
+ if data != s
384
+ raise "test failed: expect #{s.inspect} but #{data.inspect}"
385
+ end
386
+ end
387
+
388
+ unless src.empty?
389
+ raise "test failed: lost records"
390
+ end
391
+
392
+ ensure
393
+ ulog.close
394
+ end
395
+
396
+ puts "ok"
397
+ end
398
+