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,244 @@
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
+ require 'msgpack/rpc'
19
+ require 'optparse'
20
+
21
+ op = OptionParser.new
22
+
23
+ (class<<self;self;end).module_eval do
24
+ define_method(:usage) do |msg|
25
+ puts op.to_s
26
+ puts "error: #{msg}" if msg
27
+ exit 1
28
+ end
29
+ end
30
+
31
+ CS_PARAMS = %w[
32
+ nid address name rsid rsids location
33
+ ]
34
+
35
+ DS_PARAMS = %w[
36
+ read write delete items
37
+ time uptime pid version
38
+ ]
39
+
40
+ EXT_PARAMS = %w[
41
+ state
42
+ ]
43
+
44
+ ALL_PARAMS = CS_PARAMS + EXT_PARAMS + DS_PARAMS
45
+
46
+ DEFAULT_PARAMS = %w[
47
+ nid address name read write delete time
48
+ ]
49
+
50
+ conf = {
51
+ :array => false,
52
+ :format => 'tsv',
53
+ :only => [],
54
+ }
55
+
56
+ op.banner = <<EOF
57
+ Usage: #{File.basename($0)} host[:port] [options] params...
58
+ params:
59
+ nid address name rsid location
60
+ state time uptime pid version
61
+ read write delete items
62
+ default params:
63
+ #{DEFAULT_PARAMS.join(' ')}
64
+ options:
65
+ EOF
66
+
67
+ op.on('-a', '--array', 'print as arrays instead of a maps', TrueClass) {|b|
68
+ conf[:array] = true
69
+ }
70
+
71
+ op.on('-o', '--only NID_OR_NAMES', 'get status of these servers only') {|s|
72
+ only = s.split(',').map {|str|
73
+ num = str.to_i
74
+ if num.to_s == str
75
+ num # nid
76
+ else
77
+ str # name
78
+ end
79
+ }
80
+ conf[:only].concat(only)
81
+ }
82
+
83
+ op.on('-t', '--tsv', 'use Tab-Separated-Values format (default)', TrueClass) {|b|
84
+ conf[:format] = 'tsv'
85
+ }
86
+
87
+ op.on('-j', '--json', 'use JSON format', TrueClass) {|b|
88
+ conf[:format] = 'json'
89
+ }
90
+
91
+ op.on('-m', '--msgpack', 'use MessagePack format', TrueClass) {|b|
92
+ conf[:format] = 'msgpack'
93
+ }
94
+
95
+ op.on('-y', '--yaml', 'use YAML format', TrueClass) {|b|
96
+ conf[:format] = 'yaml'
97
+ }
98
+
99
+ op.on('-a', '--array', 'show as array instead of map', TrueClass) {|b|
100
+ conf[:array] = true
101
+ }
102
+
103
+ begin
104
+ op.parse!(ARGV)
105
+
106
+ if ARGV.empty?
107
+ usage nil
108
+ end
109
+
110
+ host, port = ARGV.shift.split(':',2)
111
+ port ||= 18700
112
+
113
+ ARGV.each {|arg|
114
+ unless ALL_PARAMS.include?(arg)
115
+ raise "unknown parameter: #{arg.dump}"
116
+ end
117
+ }
118
+ params = ARGV
119
+
120
+ if params.empty?
121
+ params = DEFAULT_PARAMS
122
+ end
123
+
124
+ rescue
125
+ usage $!.to_s
126
+ end
127
+
128
+ sp = MessagePack::RPC::SessionPool.new
129
+ cs = sp.get_session(host, port)
130
+
131
+ Node = Struct.new('Node', *ALL_PARAMS.map{|s|s.to_sym})
132
+
133
+ nodes = cs.call(:stat, 'nodes').map {|nid,address,name,rsids,location|
134
+ address = MessagePack::RPC::Address.load(address)
135
+ n = Node.new
136
+ n.nid = nid
137
+ n.address = address.to_s
138
+ n.name = name
139
+ n.rsids = rsids
140
+ n.rsid = rsids[0]
141
+ n.location = location
142
+ n
143
+ }.sort_by {|node|
144
+ node.nid
145
+ }
146
+
147
+ only = conf[:only]
148
+ unless only.empty?
149
+ nodes.select! {|n|
150
+ only.include?(n.nid) || only.include?(n.name)
151
+ }
152
+ end
153
+
154
+ if params.include?('state')
155
+ fault_nids = cs.call(:stat, 'fault')
156
+ nodes.each {|n|
157
+ if fault_nids.include?(n.nid)
158
+ n.state = 'FAULT'
159
+ else
160
+ n.state = 'active'
161
+ end
162
+ }
163
+ end
164
+
165
+ ds_params = params.select {|pa| DS_PARAMS.include?(pa) }
166
+ unless ds_params.empty?
167
+ stats = nodes.map {|n|
168
+ fs = []
169
+ s = sp.get_session(*n.address.split(':',2))
170
+ ds_params.each {|pa|
171
+ case pa
172
+ when "read"
173
+ fs << s.call_async(:stat, 'cmd_read')
174
+ when "write"
175
+ fs << s.call_async(:stat, 'cmd_write')
176
+ when "delete"
177
+ fs << s.call_async(:stat, 'cmd_delete')
178
+ when "items"
179
+ fs << s.call_async(:stat, 'db_items')
180
+ when "time"
181
+ fs << s.call_async(:stat, 'time')
182
+ when "uptime"
183
+ fs << s.call_async(:stat, 'uptime')
184
+ when "pid"
185
+ fs << s.call_async(:stat, 'pid')
186
+ when "version"
187
+ fs << s.call_async(:stat, 'version')
188
+ end
189
+ }
190
+ fs
191
+ }.map {|fs|
192
+ fs.map {|f|
193
+ f.get rescue nil
194
+ }
195
+ }
196
+
197
+ nodes.zip(stats) {|n,stat|
198
+ ds_params.each {|pa|
199
+ n.__send__(pa+"=", stat.shift)
200
+ }
201
+ }
202
+ end
203
+
204
+ if conf[:array]
205
+ results = nodes.map {|n|
206
+ params.map {|pa|
207
+ n.__send__(pa)
208
+ }
209
+ }
210
+ else
211
+ results = nodes.map {|n|
212
+ h = []
213
+ params.map {|pa|
214
+ h << pa << n.__send__(pa)
215
+ }
216
+ Hash[*h]
217
+ }
218
+ end
219
+
220
+ case conf[:format]
221
+ when 'tsv'
222
+ if conf[:array]
223
+ results.each {|a|
224
+ $stdout.puts a.join("\t")
225
+ }
226
+ else
227
+ $stdout.puts params.join("\t")
228
+ results.each {|h|
229
+ $stdout.puts h.values.join("\t")
230
+ }
231
+ end
232
+
233
+ when 'msgpack'
234
+ $stdout.print results.to_msgpack
235
+
236
+ when 'yaml'
237
+ require 'yaml'
238
+ $stdout.print results.to_yaml
239
+
240
+ when 'json'
241
+ require 'json'
242
+ $stdout.print results.to_json
243
+ end
244
+
@@ -0,0 +1,291 @@
1
+ #
2
+ # LS4
3
+ # Copyright (C) 2010 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
+ begin
20
+ require 'rubygems'
21
+ rescue LoadError
22
+ end
23
+ require 'msgpack/rpc'
24
+ require 'optparse'
25
+ require "curses"
26
+ require 'pp'
27
+
28
+ op = OptionParser.new
29
+ op.banner += " <cs address[:port]>"
30
+
31
+ (class<<self;self;end).module_eval do
32
+ define_method(:usage) do |msg|
33
+ puts op.to_s
34
+ puts "error: #{msg}" if msg
35
+ exit 1
36
+ end
37
+ end
38
+
39
+ if ARGV.length < 1
40
+ usage(nil)
41
+ end
42
+
43
+ $net = MessagePack::RPC::SessionPool.new
44
+ addr = ARGV.shift
45
+ host, port = addr.split(':', 2)
46
+ port = port.to_i
47
+ port = 18700 if port == 0
48
+ cs_addr = [host,port]#Address.new(host, port)
49
+
50
+ begin
51
+ op.parse!(ARGV)
52
+
53
+ if ARGV.length != 0
54
+ raise "unknown option: #{ARGV[0].dump}"
55
+ end
56
+
57
+ rescue
58
+ usage $!.to_s
59
+ end
60
+
61
+
62
+ TITLES =
63
+ # 1 2 3 4 5 6 7 8 9 10 11 12
64
+ %w[nid name address location replset #Read #Write Read/s Write/s QPS items time]
65
+
66
+ FORMAT_LARGE =
67
+ %[%1$3s %2$12s %3$23s %4$23s %5$8s %6$8s %7$8s %8$7s %9$7s %10$7s %11$10s %12$20s]
68
+
69
+ FORMAT_LARGE_SIMPLE =
70
+ %[%1$3s %2$12s%4$22s %5$8s %8$7s %9$7s %10$7s %11$10s]
71
+
72
+
73
+ FORMAT_SMALL =
74
+ # 1 2 5 6 7 11 4
75
+ %[%1$3s%2$13s %5$7s%6$9s%7$9s%11$9s%4$21s] +
76
+ %[\n %3$23s%8$9s%9$9s%10$9s%12$21s]
77
+ # 3 8 9 10 12
78
+
79
+ FORMAT_SMALL_SIMPLE =
80
+ # 1 2 5 6 7 11 4
81
+ %[%1$3s%2$13s %5$7s%6$9s%7$9s%11$9s%4$21s] +
82
+ %[\n %8$9s%9$9s%10$9s]
83
+ # 8 9 10
84
+
85
+ TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
86
+
87
+
88
+ class TargetNode
89
+ def initialize(nid, address, name, rsids, location, fault)
90
+ @nid = nid
91
+ @address = address
92
+ @name = name
93
+ @rsids = rsids
94
+ @location = location
95
+ @fault = fault
96
+ @time = Time.at(0)
97
+ @before_read = 0
98
+ @before_write = 0
99
+ @futures = []
100
+ end
101
+
102
+ attr_reader :nid
103
+
104
+ def update_info(address, name, rsids, location, fault)
105
+ @address = address
106
+ @name = name
107
+ @rsids = rsids
108
+ @location = location
109
+ @fault = fault
110
+ end
111
+
112
+ def refresh_async
113
+ s = $net.get_session(*@address)
114
+ s.timeout = 3
115
+ @futures = []
116
+ @futures[0] = s.call_async(:stat, 'cmd_read')
117
+ @futures[1] = s.call_async(:stat, 'cmd_write')
118
+ @futures[2] = s.call_async(:stat, 'time')
119
+ @futures[3] = s.call_async(:stat, 'db_items')
120
+
121
+ now = Time.now
122
+ @elapse = now - @time
123
+ @time = now
124
+
125
+ self
126
+ end
127
+
128
+ def refresh_async_get
129
+ ar = Array.new(12)
130
+ ar[0] = @nid
131
+ ar[1] = @name
132
+ ar[2] = @address.to_s
133
+ ar[3] = @location
134
+ ar[4] = @rsids.join(',')
135
+
136
+ if @fault
137
+ ar[5] = "FAULT node"
138
+ return ar
139
+ end
140
+
141
+ begin
142
+ nread = @futures[0].get
143
+ nwrite = @futures[1].get
144
+ time = @futures[2].get
145
+ db_items = @futures[3].get
146
+ psread = (nread - @before_read ) / @elapse
147
+ pswrite = (nwrite - @before_write) / @elapse
148
+
149
+ @before_read = nread
150
+ @before_write = nwrite
151
+
152
+ ar[5] = nread
153
+ ar[6] = nwrite
154
+ ar[7] = psread.to_i
155
+ ar[8] = pswrite.to_i
156
+ ar[9] = (psread + pswrite).to_i
157
+ ar[10] = db_items
158
+ ar[11] = time_format(time)
159
+
160
+ rescue
161
+ @before_read = 0
162
+ @before_write = 0
163
+ ar[5] = "error: #{$!.to_s}"
164
+ end
165
+
166
+ ar
167
+ end
168
+
169
+ def time_format(t)
170
+ if t
171
+ Time.at(t).strftime(TIME_FORMAT)
172
+ else
173
+ ""
174
+ end
175
+ end
176
+ end
177
+
178
+
179
+ class Top
180
+ def initialize(cs_addr)
181
+ @cs_addr = cs_addr
182
+ @nodes = {} # { nid => TargetNode }
183
+ @nodes_sorted = [] # [TargetNode]
184
+ update_nodes
185
+ @simple = false
186
+ end
187
+
188
+ def toggle_simple
189
+ if @simple
190
+ @simple = false
191
+ else
192
+ @simple = true
193
+ end
194
+ end
195
+
196
+ def update_nodes
197
+ s = $net.get_session(*@cs_addr)
198
+ s.timeout = 20
199
+
200
+ fault_nids = s.call(:stat, 'fault')
201
+ s.call(:stat, 'nodes').each {|nid,address,name,rsids,location|
202
+ address = MessagePack::RPC::Address.load(address)
203
+ fault = fault_nids.include?(nid)
204
+ if node = @nodes[nid]
205
+ node.update_info(address, name, rsids, location, fault)
206
+ else
207
+ @nodes[nid] = TargetNode.new(nid, address, name, rsids, location, fault)
208
+ end
209
+ }
210
+ @nodes_sorted = @nodes.values.sort_by {|node| node.nid }
211
+ end
212
+
213
+ def refresh
214
+ Curses.clear
215
+ if @simple
216
+ if Curses.stdscr.maxx >= 82
217
+ format = FORMAT_LARGE_SIMPLE
218
+ else
219
+ format = FORMAT_SMALL_SIMPLE
220
+ end
221
+ else
222
+ if Curses.stdscr.maxx >= 147
223
+ format = FORMAT_LARGE
224
+ else
225
+ format = FORMAT_SMALL
226
+ end
227
+ end
228
+
229
+ Curses.setpos(0, 0)
230
+ title_line = format % TITLES
231
+ nlines = title_line.count("\n")+1
232
+ Curses.addstr(title_line)
233
+
234
+ @nodes_sorted.each {|node|
235
+ node.refresh_async
236
+ }
237
+ @nodes_sorted.each_with_index {|node, i|
238
+ Curses.setpos((1+i)*nlines, 0)
239
+ params = node.refresh_async_get
240
+ line = format % params
241
+ Curses.addstr(line)
242
+ }
243
+
244
+ Curses.refresh
245
+ end
246
+
247
+ def run
248
+ i = 0
249
+ while true
250
+ before = Time.now
251
+
252
+ refresh
253
+
254
+ i += 1
255
+ if i > 5
256
+ i = 0
257
+ update_nodes
258
+ end
259
+
260
+ elapse = Time.now - before
261
+ if elapse < 0.5
262
+ sleep 0.5 - elapse
263
+ end
264
+ end
265
+ rescue
266
+ $stderr.puts $!.inspect
267
+ end
268
+ end
269
+
270
+ top = Top.new(cs_addr)
271
+
272
+ Curses.init_screen
273
+
274
+ begin
275
+ th = Thread.start(&top.method(:run))
276
+
277
+ while true
278
+ ch = Curses.getch
279
+ case ch
280
+ when ?s
281
+ top.toggle_simple
282
+ when ?q
283
+ break
284
+ end
285
+ #top.refresh
286
+ end
287
+
288
+ ensure
289
+ Curses.close_screen
290
+ end
291
+