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.
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
+