fairy 0.6.0 → 0.6.5
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/Makefile +1 -0
- data/bin/fairy +35 -5
- data/ext/extconf.rb +3 -0
- data/ext/fairy.c +180 -0
- data/ext/fairy.h +94 -0
- data/ext/fiber_mon.h +32 -0
- data/ext/fixnum-buffer.c +483 -0
- data/ext/p-group-by.c +529 -0
- data/ext/p-xgroup-by.c +467 -0
- data/ext/simple-hash.c +44 -0
- data/ext/string-buffer.c +286 -0
- data/ext/xmarshaled-queue.c +699 -0
- data/ext/xsized-queue.c +528 -0
- data/ext/xthread.h +65 -0
- data/fairy.gemspec +5 -2
- data/lib/fairy.rb +10 -1
- data/lib/fairy/client/group-by.rb +57 -2
- data/lib/fairy/client/here.rb +2 -1
- data/lib/fairy/controller.rb +25 -4
- data/lib/fairy/master.rb +17 -3
- data/lib/fairy/master/c-basic-group-by.rb +4 -2
- data/lib/fairy/master/c-cat.rb +3 -2
- data/lib/fairy/master/c-direct-product.rb +5 -3
- data/lib/fairy/master/c-filter.rb +5 -3
- data/lib/fairy/master/c-group-by.rb +13 -0
- data/lib/fairy/master/c-junction.rb +3 -2
- data/lib/fairy/master/c-seg-join.rb +3 -1
- data/lib/fairy/master/c-seg-shuffle.rb +3 -2
- data/lib/fairy/master/c-seg-split.rb +1 -1
- data/lib/fairy/master/c-seg-zip.rb +3 -1
- data/lib/fairy/master/c-sort.rb +7 -2
- data/lib/fairy/master/c-wc.rb +5 -3
- data/lib/fairy/node.rb +13 -2
- data/lib/fairy/node/p-barrier.rb +1 -1
- data/lib/fairy/node/p-basic-group-by.rb +22 -12
- data/lib/fairy/node/p-direct-product.rb +4 -2
- data/lib/fairy/node/p-filter.rb +8 -7
- data/lib/fairy/node/p-find.rb +2 -1
- data/lib/fairy/node/p-group-by.rb +17 -6
- data/lib/fairy/node/p-inject.rb +3 -2
- data/lib/fairy/node/p-output-file.rb +1 -1
- data/lib/fairy/node/p-seg-join.rb +2 -1
- data/lib/fairy/node/p-seg-zip.rb +2 -1
- data/lib/fairy/node/p-single-exportable.rb +3 -1
- data/lib/fairy/node/p-sort.rb +4 -2
- data/lib/fairy/node/p-task.rb +1 -1
- data/lib/fairy/node/p-wc.rb +5 -2
- data/lib/fairy/processor.rb +25 -18
- data/lib/fairy/share/block-source.rb +12 -2
- data/lib/fairy/share/conf.rb +35 -5
- data/lib/fairy/share/hash-simple-hash.rb +1 -1
- data/lib/fairy/share/log.rb +11 -4
- data/lib/fairy/share/pool-dictionary.rb +2 -1
- data/lib/fairy/share/port-marshaled-queue.rb +8 -1
- data/lib/fairy/share/port.rb +55 -45
- data/lib/fairy/share/reference.rb +2 -1
- data/lib/fairy/share/varray.rb +3 -1
- data/lib/fairy/share/vfile.rb +4 -2
- data/lib/fairy/version.rb +1 -1
- data/sample/sort.rb +69 -3
- data/spec/fairy8_spec.rb +1 -1
- data/test/testc.rb +380 -2
- data/tools/cap_recipe/Capfile +3 -3
- data/tools/fairy_conf_wizard.rb +375 -0
- data/tools/fairy_perf_graph.rb +15 -3
- data/tools/git-tag +1 -0
- data/tools/log-analysis.rb +59 -11
- metadata +33 -34
- data/ext/simple_hash/extconf.rb +0 -4
- data/ext/simple_hash/simple_hash.c +0 -42
data/tools/cap_recipe/Capfile
CHANGED
@@ -92,12 +92,12 @@ on all hosts.
|
|
92
92
|
DESC
|
93
93
|
task :install, :roles => [:master, :nodes] do
|
94
94
|
pkg = "fairy"
|
95
|
-
if local_pkg
|
95
|
+
if exists? :local_pkg
|
96
96
|
pkg = "/tmp/#{File.basename(local_pkg)}"
|
97
97
|
upload "#{local_pkg}", "#{pkg}"
|
98
98
|
end
|
99
99
|
|
100
|
-
if gem_cmd
|
100
|
+
if exists? :gem_cmd
|
101
101
|
sudo %{ #{gem_cmd} install #{pkg} }
|
102
102
|
else
|
103
103
|
sudo %{ gem install #{pkg} }
|
@@ -134,7 +134,7 @@ Update fairy on all hosts in the cluster.
|
|
134
134
|
You need to grant "sudo" privilege to "run_uid" user.
|
135
135
|
DESC
|
136
136
|
task :update, :roles => [:master, :nodes] do
|
137
|
-
if gem_cmd
|
137
|
+
if exists? :gem_cmd
|
138
138
|
sudo %{ #{gem_cmd} update fairy }
|
139
139
|
else
|
140
140
|
sudo %{ gem update fairy }
|
@@ -0,0 +1,375 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
#
|
4
|
+
# Configuration File Generator for fairy
|
5
|
+
#
|
6
|
+
# Copyright (C) 2011 Rakuten, Inc.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pp'
|
10
|
+
require 'readline'
|
11
|
+
require 'fileutils'
|
12
|
+
|
13
|
+
|
14
|
+
class FairyConfWizard
|
15
|
+
|
16
|
+
GRP_BY_N_SEG_FACTOR = 4 # This is *heuristic* value.
|
17
|
+
|
18
|
+
def self.splash
|
19
|
+
#1234567890123456789012345678901234567890123
|
20
|
+
puts "*" * 43
|
21
|
+
puts "** Configuration File Generator for fairy"
|
22
|
+
puts "** (C) 2011 Rakuten, Inc."
|
23
|
+
puts "*" * 43
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
unless fairy_home = ENV["FAIRY_HOME"]
|
28
|
+
fairy_home = "/home/fairy"
|
29
|
+
end
|
30
|
+
|
31
|
+
tmpl_path = fairy_home + "/etc/fairy.conf.tmpl"
|
32
|
+
if FileTest.file?(tmpl_path) && FileTest.readable?(tmpl_path)
|
33
|
+
ask_something(
|
34
|
+
"Do you want to use #{tmpl_path} for the template?",
|
35
|
+
:acceptable => %w{y n},
|
36
|
+
:default => "y"
|
37
|
+
){|input, opt|
|
38
|
+
if input.downcase == "y"
|
39
|
+
begin
|
40
|
+
@tmpl = File.open(tmpl_path, "r")
|
41
|
+
rescue => e
|
42
|
+
puts e.message
|
43
|
+
@tmpl = nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
true
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
unless @tmpl
|
51
|
+
ask_something(
|
52
|
+
"Where is your template for fairy.conf?"
|
53
|
+
){|input, opt|
|
54
|
+
begin
|
55
|
+
@tmpl = File.open(input, "r")
|
56
|
+
true
|
57
|
+
rescue => e
|
58
|
+
puts e.message
|
59
|
+
false
|
60
|
+
end
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
@conf_path = fairy_home + "/etc/fairy.conf"
|
65
|
+
@master_host = "localhost"
|
66
|
+
@master_port = 19999
|
67
|
+
@group_by_n_seg = 1
|
68
|
+
#@ext_disable = false
|
69
|
+
@vf_root = fairy_home + "/Repos"
|
70
|
+
@tmp_dir = "/tmp/fairy/tmpbuf"
|
71
|
+
@log_file = "/tmp/fairy/log"
|
72
|
+
@opt_enable = false
|
73
|
+
@prompt = "> "
|
74
|
+
end
|
75
|
+
|
76
|
+
def ask_something(question, opt={})
|
77
|
+
acceptable = opt[:acceptable]
|
78
|
+
default = opt[:default]
|
79
|
+
once = opt[:once]
|
80
|
+
integer = opt[:integer]
|
81
|
+
|
82
|
+
str = " "
|
83
|
+
if acceptable
|
84
|
+
str << "["
|
85
|
+
str << acceptable.map{|it|
|
86
|
+
ret = it.downcase
|
87
|
+
if default && (ret == default)
|
88
|
+
ret.upcase!
|
89
|
+
end
|
90
|
+
ret
|
91
|
+
}.join("/")
|
92
|
+
str << "]"
|
93
|
+
elsif default
|
94
|
+
str << "[%s]" % default
|
95
|
+
end
|
96
|
+
|
97
|
+
loop do
|
98
|
+
$stdout.write(question)
|
99
|
+
$stdout.write(str + "\n")
|
100
|
+
$stdout.flush
|
101
|
+
|
102
|
+
input = Readline.readline("> ", true)
|
103
|
+
#input.chomp!
|
104
|
+
input.rstrip!
|
105
|
+
|
106
|
+
if default && input.empty?
|
107
|
+
input = default
|
108
|
+
end
|
109
|
+
|
110
|
+
if integer
|
111
|
+
begin
|
112
|
+
#input = input.to_i
|
113
|
+
input = Integer(input)
|
114
|
+
rescue
|
115
|
+
puts "WRONG number!" % input
|
116
|
+
redo
|
117
|
+
end
|
118
|
+
if (integer == :positive) && (input < 0)
|
119
|
+
puts "The number must be POSITIVE!"
|
120
|
+
redo
|
121
|
+
elsif (integer == :negative) && (input >= 0)
|
122
|
+
puts "The number must be NAGATIVE!"
|
123
|
+
redo
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
if acceptable && !acceptable.map{|it| it.downcase}.include?(input.downcase)
|
128
|
+
redo
|
129
|
+
end
|
130
|
+
|
131
|
+
if block_given?
|
132
|
+
ret = yield(input, opt)
|
133
|
+
else
|
134
|
+
ret = true
|
135
|
+
end
|
136
|
+
|
137
|
+
if ret || once
|
138
|
+
return ret
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def ask_test1
|
144
|
+
ask_something(
|
145
|
+
"Do you like pizza?",
|
146
|
+
:acceptable => %w{y n},
|
147
|
+
:default => "y"
|
148
|
+
){|input, opt|
|
149
|
+
if input.downcase == "y"
|
150
|
+
puts "Yes, you do."
|
151
|
+
else
|
152
|
+
puts "No, you don't."
|
153
|
+
end
|
154
|
+
true
|
155
|
+
}
|
156
|
+
end
|
157
|
+
|
158
|
+
def ask_test2
|
159
|
+
ask_something(
|
160
|
+
"Which kind of food do you like?",
|
161
|
+
:default => "pizza"
|
162
|
+
){|input, opt|
|
163
|
+
if input.downcase == "sushi"
|
164
|
+
puts "Sorry, I don't have any sushi. Tell me another one."
|
165
|
+
false
|
166
|
+
else
|
167
|
+
puts "You like %s." % input
|
168
|
+
true
|
169
|
+
end
|
170
|
+
}
|
171
|
+
end
|
172
|
+
|
173
|
+
def ask_conf_path
|
174
|
+
ask_something(
|
175
|
+
"Which path do you want to write new fairy.conf into?",
|
176
|
+
:default => @conf_path
|
177
|
+
){|input, opt|
|
178
|
+
input = File.expand_path(input)
|
179
|
+
#p input
|
180
|
+
if FileTest.exist?(input)
|
181
|
+
puts "%s already exists." % input
|
182
|
+
ask_something(
|
183
|
+
"Do you want to override existing file?",
|
184
|
+
:acceptable => %w{y n},
|
185
|
+
:default => "n",
|
186
|
+
:once => true,
|
187
|
+
:super => input
|
188
|
+
){|input, opt|
|
189
|
+
if input.downcase == "y"
|
190
|
+
@conf_path = opt[:super]
|
191
|
+
true
|
192
|
+
else
|
193
|
+
false
|
194
|
+
end
|
195
|
+
}
|
196
|
+
else
|
197
|
+
@conf_path = input
|
198
|
+
true
|
199
|
+
end
|
200
|
+
}
|
201
|
+
end
|
202
|
+
|
203
|
+
def ask_master_host
|
204
|
+
ask_something(
|
205
|
+
"Enter your master server name.",
|
206
|
+
:default => @master_host
|
207
|
+
){|input, opt|
|
208
|
+
@master_host = input
|
209
|
+
true
|
210
|
+
}
|
211
|
+
end
|
212
|
+
|
213
|
+
def ask_master_port
|
214
|
+
ask_something(
|
215
|
+
"Which port your master server listen to?",
|
216
|
+
:default => @master_port,
|
217
|
+
:integer => :positive
|
218
|
+
){|input, opt|
|
219
|
+
@master_port = input
|
220
|
+
true
|
221
|
+
}
|
222
|
+
end
|
223
|
+
|
224
|
+
def ask_no_of_nodes
|
225
|
+
ask_something(
|
226
|
+
"How many CPUs (cores) does your cluster have?",
|
227
|
+
:integer => :positive
|
228
|
+
){|input, opt|
|
229
|
+
@group_by_n_seg = input * GRP_BY_N_SEG_FACTOR
|
230
|
+
true
|
231
|
+
}
|
232
|
+
end
|
233
|
+
|
234
|
+
# def ask_ext_disable
|
235
|
+
# ask_something(
|
236
|
+
# "Do you want to use extentions written in C? (Faster, strongly recommended)",
|
237
|
+
# :acceptable => %w{y n},
|
238
|
+
# :default => "y"
|
239
|
+
# ){|input, opt|
|
240
|
+
# if input.downcase == "n"
|
241
|
+
# @ext_disable = true;
|
242
|
+
# end
|
243
|
+
# true
|
244
|
+
# }
|
245
|
+
# end
|
246
|
+
|
247
|
+
def ask_vf_root
|
248
|
+
ask_something(
|
249
|
+
"Which directory do you want to put data (VFile segments) into?\nYou can embed Ruby code with \#{ ... } style.",
|
250
|
+
:default => @vf_root
|
251
|
+
){|input, opt|
|
252
|
+
@vf_root = input
|
253
|
+
true
|
254
|
+
}
|
255
|
+
end
|
256
|
+
|
257
|
+
def ask_tmp_dir
|
258
|
+
ask_something(
|
259
|
+
"Which directory do you want to use for temporary directory?\nYou can embed Ruby code with \#{ ... } style.",
|
260
|
+
:default => @tmp_dir
|
261
|
+
){|input, opt|
|
262
|
+
@tmp_dir = input
|
263
|
+
true
|
264
|
+
}
|
265
|
+
end
|
266
|
+
|
267
|
+
def ask_log_file
|
268
|
+
ask_something(
|
269
|
+
"Which path do you want to put master server's log file into?\nYou can embed Ruby code with \#{ ... } style.",
|
270
|
+
:default => @log_file
|
271
|
+
){|input, opt|
|
272
|
+
@log_file = input
|
273
|
+
true
|
274
|
+
}
|
275
|
+
end
|
276
|
+
|
277
|
+
def ask_opt_enable
|
278
|
+
ask_something(
|
279
|
+
"Do you want to turn some optimizations on?\nThat makes fairy faster in some situations. But fairy may become unstable",
|
280
|
+
:acceptable => %w{y n},
|
281
|
+
:default => "n"
|
282
|
+
){|input, opt|
|
283
|
+
if input.downcase == "y"
|
284
|
+
@opt_enable = true;
|
285
|
+
end
|
286
|
+
true
|
287
|
+
}
|
288
|
+
end
|
289
|
+
|
290
|
+
def commit
|
291
|
+
#pp self
|
292
|
+
|
293
|
+
conf = File.open(@conf_path + ".part", "w")
|
294
|
+
|
295
|
+
entries = {
|
296
|
+
"MASTER_HOST" => [@master_host, {:skip => 1}],
|
297
|
+
"MASTER_PORT" => [@master_port],
|
298
|
+
"GROUP_BY_NO_SEGMENT" => [@group_by_n_seg],
|
299
|
+
"SORT_NO_SEGMENT" => ["CONF.GROUP_BY_NO_SEGMENT"],
|
300
|
+
"VF_ROOT" => [@vf_root, {:embed_ruby => true}],
|
301
|
+
"TMP_DIR" => [@tmp_dir, {:embed_ruby => true}],
|
302
|
+
"LOG_FILE" => [@log_file, {:embed_ruby => true}],
|
303
|
+
}
|
304
|
+
|
305
|
+
if @opt_enable
|
306
|
+
entries["GROUP_BY_GROUPING_OPTIMIZE"] = [true];
|
307
|
+
entries["SORT_CMP_OPTIMIZE"] = [true];
|
308
|
+
entries["BLOCK_USE_STDOUT"] = [false];
|
309
|
+
end
|
310
|
+
|
311
|
+
@tmpl.each{|ln|
|
312
|
+
ln.chomp!
|
313
|
+
conf.puts ln
|
314
|
+
|
315
|
+
entries.each {|name,ary|
|
316
|
+
if ln.match(%r{\A\s*##CONF\.#{name}\s*=})
|
317
|
+
puts_entry(conf, name, ary[0], ary[1])
|
318
|
+
|
319
|
+
entries.delete(name)
|
320
|
+
break
|
321
|
+
end
|
322
|
+
}
|
323
|
+
}
|
324
|
+
|
325
|
+
conf.close
|
326
|
+
FileUtils.mv(conf.path, @conf_path)
|
327
|
+
end
|
328
|
+
|
329
|
+
def puts_entry(io, name, value, opt)
|
330
|
+
opt ||= {}
|
331
|
+
|
332
|
+
if value.kind_of?(String) && opt[:embed_ruby]
|
333
|
+
strval = %{"#{value.gsub('"', '\\"')}"}
|
334
|
+
else
|
335
|
+
strval = value.inspect
|
336
|
+
end
|
337
|
+
|
338
|
+
io.puts "CONF.#{name} = #{strval}"
|
339
|
+
|
340
|
+
if opt[:skip]
|
341
|
+
opt[:skip].times{
|
342
|
+
@tmpl.gets
|
343
|
+
}
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
def run
|
348
|
+
puts "Template: #{@tmpl.path}"
|
349
|
+
|
350
|
+
#ask_test1
|
351
|
+
#ask_test2
|
352
|
+
ask_conf_path
|
353
|
+
ask_master_host
|
354
|
+
ask_master_port
|
355
|
+
ask_no_of_nodes
|
356
|
+
#ask_ext_disable
|
357
|
+
ask_vf_root
|
358
|
+
ask_tmp_dir
|
359
|
+
ask_log_file
|
360
|
+
ask_opt_enable
|
361
|
+
|
362
|
+
puts "Writing into: #{@conf_path}"
|
363
|
+
commit
|
364
|
+
|
365
|
+
puts "done."
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
|
370
|
+
FairyConfWizard.splash
|
371
|
+
wzd = FairyConfWizard.new
|
372
|
+
wzd.run
|
373
|
+
|
374
|
+
|
375
|
+
|
data/tools/fairy_perf_graph.rb
CHANGED
@@ -86,6 +86,7 @@ module FairyPerformanceGraph
|
|
86
86
|
|
87
87
|
@contents.each{|node|
|
88
88
|
node.processors.each{|processor|
|
89
|
+
next if processor.nil?
|
89
90
|
processor.filters.each{|filter|
|
90
91
|
@start_at = filter.start_at if @start_at > filter.start_at
|
91
92
|
@end_at = filter.end_at if @end_at < filter.end_at
|
@@ -150,6 +151,13 @@ module FairyPerformanceGraph
|
|
150
151
|
|
151
152
|
@name = name
|
152
153
|
@contents = []
|
154
|
+
@contents.instance_eval {
|
155
|
+
def get_by_id(id)
|
156
|
+
self.find{|prc|
|
157
|
+
prc.id == id
|
158
|
+
}
|
159
|
+
end
|
160
|
+
}
|
153
161
|
|
154
162
|
@margin_left = 80
|
155
163
|
|
@@ -278,6 +286,7 @@ module FairyPerformanceGraph
|
|
278
286
|
@start_at = Time.parse(start_at)
|
279
287
|
@end_at = Time.parse(end_at)
|
280
288
|
@elapsed = elapsed
|
289
|
+
@elapsed_for_store = 0;
|
281
290
|
|
282
291
|
@fgcolor = "BLACK"
|
283
292
|
@font_size = 8
|
@@ -323,6 +332,7 @@ module FairyPerformanceGraph
|
|
323
332
|
if @type == IMPORT
|
324
333
|
context.set_source_color("CORNFLOWER_BLUE")
|
325
334
|
width_store = (@elapsed_for_store * scale).to_i
|
335
|
+
#width_store = ((@elapsed_for_store.nil? ? 0 : @elapsed_for_store) * scale).to_i
|
326
336
|
off_x_store = off_x + width - width_store
|
327
337
|
context.rectangle(off_x_store, y, width_store, h)
|
328
338
|
context.fill
|
@@ -391,8 +401,10 @@ module FairyPerformanceGraph
|
|
391
401
|
|
392
402
|
if type == "STORE"
|
393
403
|
node = graph.nodes.select{|node| node.name == host_name}[0] or next
|
394
|
-
processor = node.processors
|
395
|
-
filter = processor.filters.select{|filter|
|
404
|
+
processor = node.processors.get_by_id(processor_id) or next
|
405
|
+
filter = processor.filters.select{|filter|
|
406
|
+
(filter.type == Filter::IMPORT) && (filter.job_id == Filter.parse_name(filter_name)[0])
|
407
|
+
}[0] or next
|
396
408
|
filter.elapsed_for_store = elapsed
|
397
409
|
#$stderr.puts("set filter.elapsed_for_store (#{filter.name})")
|
398
410
|
next
|
@@ -405,7 +417,7 @@ module FairyPerformanceGraph
|
|
405
417
|
graph.nodes << node
|
406
418
|
end
|
407
419
|
|
408
|
-
unless processor = node.processors
|
420
|
+
unless processor = node.processors.get_by_id(processor_id)
|
409
421
|
processor = Processor.new(node, processor_id)
|
410
422
|
node.processors << processor
|
411
423
|
end
|