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.
Files changed (70) hide show
  1. data/Makefile +1 -0
  2. data/bin/fairy +35 -5
  3. data/ext/extconf.rb +3 -0
  4. data/ext/fairy.c +180 -0
  5. data/ext/fairy.h +94 -0
  6. data/ext/fiber_mon.h +32 -0
  7. data/ext/fixnum-buffer.c +483 -0
  8. data/ext/p-group-by.c +529 -0
  9. data/ext/p-xgroup-by.c +467 -0
  10. data/ext/simple-hash.c +44 -0
  11. data/ext/string-buffer.c +286 -0
  12. data/ext/xmarshaled-queue.c +699 -0
  13. data/ext/xsized-queue.c +528 -0
  14. data/ext/xthread.h +65 -0
  15. data/fairy.gemspec +5 -2
  16. data/lib/fairy.rb +10 -1
  17. data/lib/fairy/client/group-by.rb +57 -2
  18. data/lib/fairy/client/here.rb +2 -1
  19. data/lib/fairy/controller.rb +25 -4
  20. data/lib/fairy/master.rb +17 -3
  21. data/lib/fairy/master/c-basic-group-by.rb +4 -2
  22. data/lib/fairy/master/c-cat.rb +3 -2
  23. data/lib/fairy/master/c-direct-product.rb +5 -3
  24. data/lib/fairy/master/c-filter.rb +5 -3
  25. data/lib/fairy/master/c-group-by.rb +13 -0
  26. data/lib/fairy/master/c-junction.rb +3 -2
  27. data/lib/fairy/master/c-seg-join.rb +3 -1
  28. data/lib/fairy/master/c-seg-shuffle.rb +3 -2
  29. data/lib/fairy/master/c-seg-split.rb +1 -1
  30. data/lib/fairy/master/c-seg-zip.rb +3 -1
  31. data/lib/fairy/master/c-sort.rb +7 -2
  32. data/lib/fairy/master/c-wc.rb +5 -3
  33. data/lib/fairy/node.rb +13 -2
  34. data/lib/fairy/node/p-barrier.rb +1 -1
  35. data/lib/fairy/node/p-basic-group-by.rb +22 -12
  36. data/lib/fairy/node/p-direct-product.rb +4 -2
  37. data/lib/fairy/node/p-filter.rb +8 -7
  38. data/lib/fairy/node/p-find.rb +2 -1
  39. data/lib/fairy/node/p-group-by.rb +17 -6
  40. data/lib/fairy/node/p-inject.rb +3 -2
  41. data/lib/fairy/node/p-output-file.rb +1 -1
  42. data/lib/fairy/node/p-seg-join.rb +2 -1
  43. data/lib/fairy/node/p-seg-zip.rb +2 -1
  44. data/lib/fairy/node/p-single-exportable.rb +3 -1
  45. data/lib/fairy/node/p-sort.rb +4 -2
  46. data/lib/fairy/node/p-task.rb +1 -1
  47. data/lib/fairy/node/p-wc.rb +5 -2
  48. data/lib/fairy/processor.rb +25 -18
  49. data/lib/fairy/share/block-source.rb +12 -2
  50. data/lib/fairy/share/conf.rb +35 -5
  51. data/lib/fairy/share/hash-simple-hash.rb +1 -1
  52. data/lib/fairy/share/log.rb +11 -4
  53. data/lib/fairy/share/pool-dictionary.rb +2 -1
  54. data/lib/fairy/share/port-marshaled-queue.rb +8 -1
  55. data/lib/fairy/share/port.rb +55 -45
  56. data/lib/fairy/share/reference.rb +2 -1
  57. data/lib/fairy/share/varray.rb +3 -1
  58. data/lib/fairy/share/vfile.rb +4 -2
  59. data/lib/fairy/version.rb +1 -1
  60. data/sample/sort.rb +69 -3
  61. data/spec/fairy8_spec.rb +1 -1
  62. data/test/testc.rb +380 -2
  63. data/tools/cap_recipe/Capfile +3 -3
  64. data/tools/fairy_conf_wizard.rb +375 -0
  65. data/tools/fairy_perf_graph.rb +15 -3
  66. data/tools/git-tag +1 -0
  67. data/tools/log-analysis.rb +59 -11
  68. metadata +33 -34
  69. data/ext/simple_hash/extconf.rb +0 -4
  70. data/ext/simple_hash/simple_hash.c +0 -42
@@ -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
+
@@ -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[processor_id] or next
395
- filter = processor.filters.select{|filter| (filter.type == Filter::IMPORT) && (filter.job_id == Filter.parse_name(filter_name)[0])}[0] or next
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[processor_id]
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