fairy 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +674 -0
- data/Makefile +116 -0
- data/README +15 -0
- data/bin/fairy +582 -0
- data/bin/fairy-cat +74 -0
- data/bin/fairy-cp +128 -0
- data/bin/fairy-rm +122 -0
- data/bin/subcmd/controller +41 -0
- data/bin/subcmd/inspector +81 -0
- data/bin/subcmd/master +43 -0
- data/bin/subcmd/node +47 -0
- data/bin/subcmd/processor +54 -0
- data/doc/programming-interface.html +240 -0
- data/doc/programming-interface.rd +300 -0
- data/etc/fairy.conf.tmpl +118 -0
- data/ext/simple_hash/extconf.rb +4 -0
- data/ext/simple_hash/simple_hash.c +42 -0
- data/fairy.gemspec +60 -0
- data/lib/fairy/client/addins.rb +20 -0
- data/lib/fairy/client/barrier.rb +29 -0
- data/lib/fairy/client/basic-group-by.rb +52 -0
- data/lib/fairy/client/cat.rb +41 -0
- data/lib/fairy/client/direct-product.rb +51 -0
- data/lib/fairy/client/equijoin.rb +79 -0
- data/lib/fairy/client/exec.rb +54 -0
- data/lib/fairy/client/filter.rb +62 -0
- data/lib/fairy/client/find.rb +35 -0
- data/lib/fairy/client/group-by.rb +194 -0
- data/lib/fairy/client/here.rb +84 -0
- data/lib/fairy/client/inject.rb +70 -0
- data/lib/fairy/client/input-file.rb +53 -0
- data/lib/fairy/client/input-iota.rb +49 -0
- data/lib/fairy/client/input-local-file.rb +188 -0
- data/lib/fairy/client/input-varray.rb +30 -0
- data/lib/fairy/client/input.rb +42 -0
- data/lib/fairy/client/io-filter.rb +26 -0
- data/lib/fairy/client/junction.rb +31 -0
- data/lib/fairy/client/map.rb +34 -0
- data/lib/fairy/client/merge-group-by.rb +71 -0
- data/lib/fairy/client/output-file.rb +64 -0
- data/lib/fairy/client/output-local-file.rb +60 -0
- data/lib/fairy/client/output-null.rb +47 -0
- data/lib/fairy/client/output-varray.rb +50 -0
- data/lib/fairy/client/output.rb +29 -0
- data/lib/fairy/client/roma-put.rb +62 -0
- data/lib/fairy/client/roma.rb +156 -0
- data/lib/fairy/client/seg-join.rb +61 -0
- data/lib/fairy/client/seg-map.rb +78 -0
- data/lib/fairy/client/seg-shuffle.rb +35 -0
- data/lib/fairy/client/seg-split.rb +27 -0
- data/lib/fairy/client/seg-zip.rb +60 -0
- data/lib/fairy/client/select.rb +38 -0
- data/lib/fairy/client/sort.rb +48 -0
- data/lib/fairy/client/sort18.rb +56 -0
- data/lib/fairy/client/sort19.rb +61 -0
- data/lib/fairy/client/there.rb +47 -0
- data/lib/fairy/client/top_n_into_roma.rb +34 -0
- data/lib/fairy/client/wc.rb +92 -0
- data/lib/fairy/controller.rb +1103 -0
- data/lib/fairy/logger.rb +107 -0
- data/lib/fairy/master/addins.rb +20 -0
- data/lib/fairy/master/atom.rb +17 -0
- data/lib/fairy/master/c-barrier.rb +283 -0
- data/lib/fairy/master/c-basic-group-by.rb +250 -0
- data/lib/fairy/master/c-cat.rb +159 -0
- data/lib/fairy/master/c-direct-product.rb +203 -0
- data/lib/fairy/master/c-exec.rb +68 -0
- data/lib/fairy/master/c-filter.rb +422 -0
- data/lib/fairy/master/c-find.rb +138 -0
- data/lib/fairy/master/c-group-by.rb +64 -0
- data/lib/fairy/master/c-here.rb +80 -0
- data/lib/fairy/master/c-inject.rb +119 -0
- data/lib/fairy/master/c-input-file.rb +46 -0
- data/lib/fairy/master/c-input-iota.rb +66 -0
- data/lib/fairy/master/c-input-local-file.rb +117 -0
- data/lib/fairy/master/c-input-varray.rb +53 -0
- data/lib/fairy/master/c-input.rb +24 -0
- data/lib/fairy/master/c-inputtable.rb +31 -0
- data/lib/fairy/master/c-inputtable18.rb +36 -0
- data/lib/fairy/master/c-inputtable19.rb +35 -0
- data/lib/fairy/master/c-io-filter.rb +28 -0
- data/lib/fairy/master/c-junction.rb +54 -0
- data/lib/fairy/master/c-map.rb +27 -0
- data/lib/fairy/master/c-merge-group-by.rb +241 -0
- data/lib/fairy/master/c-output-file.rb +84 -0
- data/lib/fairy/master/c-output-local-file.rb +19 -0
- data/lib/fairy/master/c-output-null.rb +45 -0
- data/lib/fairy/master/c-output-varray.rb +57 -0
- data/lib/fairy/master/c-output.rb +20 -0
- data/lib/fairy/master/c-seg-join.rb +141 -0
- data/lib/fairy/master/c-seg-map.rb +26 -0
- data/lib/fairy/master/c-seg-shuffle.rb +87 -0
- data/lib/fairy/master/c-seg-split.rb +110 -0
- data/lib/fairy/master/c-seg-zip.rb +132 -0
- data/lib/fairy/master/c-select.rb +27 -0
- data/lib/fairy/master/c-sort.rb +108 -0
- data/lib/fairy/master/c-there.rb +57 -0
- data/lib/fairy/master/c-wc.rb +232 -0
- data/lib/fairy/master/job-interpriter.rb +19 -0
- data/lib/fairy/master/scheduler.rb +24 -0
- data/lib/fairy/master.rb +329 -0
- data/lib/fairy/node/addins.rb +19 -0
- data/lib/fairy/node/p-barrier.rb +95 -0
- data/lib/fairy/node/p-basic-group-by.rb +252 -0
- data/lib/fairy/node/p-direct-product.rb +153 -0
- data/lib/fairy/node/p-exec.rb +30 -0
- data/lib/fairy/node/p-filter.rb +363 -0
- data/lib/fairy/node/p-find.rb +111 -0
- data/lib/fairy/node/p-group-by.rb +1534 -0
- data/lib/fairy/node/p-here.rb +21 -0
- data/lib/fairy/node/p-identity.rb +24 -0
- data/lib/fairy/node/p-inject.rb +127 -0
- data/lib/fairy/node/p-input-file.rb +108 -0
- data/lib/fairy/node/p-input-iota.rb +39 -0
- data/lib/fairy/node/p-input-local-file.rb +61 -0
- data/lib/fairy/node/p-input-varray.rb +26 -0
- data/lib/fairy/node/p-io-filter.rb +28 -0
- data/lib/fairy/node/p-map.rb +40 -0
- data/lib/fairy/node/p-merger-group-by.rb +48 -0
- data/lib/fairy/node/p-output-file.rb +104 -0
- data/lib/fairy/node/p-output-local-file.rb +14 -0
- data/lib/fairy/node/p-output-null.rb +32 -0
- data/lib/fairy/node/p-output-varray.rb +41 -0
- data/lib/fairy/node/p-seg-join.rb +82 -0
- data/lib/fairy/node/p-seg-map.rb +34 -0
- data/lib/fairy/node/p-seg-split.rb +61 -0
- data/lib/fairy/node/p-seg-zip.rb +79 -0
- data/lib/fairy/node/p-select.rb +40 -0
- data/lib/fairy/node/p-single-exportable.rb +90 -0
- data/lib/fairy/node/p-sort.rb +195 -0
- data/lib/fairy/node/p-task.rb +113 -0
- data/lib/fairy/node/p-there.rb +44 -0
- data/lib/fairy/node/p-wc.rb +266 -0
- data/lib/fairy/node.rb +187 -0
- data/lib/fairy/processor.rb +510 -0
- data/lib/fairy/share/base-app.rb +114 -0
- data/lib/fairy/share/block-source.rb +234 -0
- data/lib/fairy/share/conf.rb +396 -0
- data/lib/fairy/share/debug.rb +21 -0
- data/lib/fairy/share/encoding.rb +17 -0
- data/lib/fairy/share/fast-tempfile.rb +93 -0
- data/lib/fairy/share/file-place.rb +176 -0
- data/lib/fairy/share/hash-1.rb +20 -0
- data/lib/fairy/share/hash-md5.rb +28 -0
- data/lib/fairy/share/hash-murmur.rb +69 -0
- data/lib/fairy/share/hash-rb18.rb +20 -0
- data/lib/fairy/share/hash-simple-hash.rb +28 -0
- data/lib/fairy/share/inspector.rb +16 -0
- data/lib/fairy/share/lc/exceptions.rb +82 -0
- data/lib/fairy/share/lc/ja/exceptions.rb +81 -0
- data/lib/fairy/share/locale.rb +17 -0
- data/lib/fairy/share/log.rb +215 -0
- data/lib/fairy/share/pool-dictionary.rb +53 -0
- data/lib/fairy/share/port-marshaled-queue.rb +347 -0
- data/lib/fairy/share/port.rb +1697 -0
- data/lib/fairy/share/reference.rb +45 -0
- data/lib/fairy/share/stdout.rb +56 -0
- data/lib/fairy/share/tr.rb +16 -0
- data/lib/fairy/share/varray.rb +147 -0
- data/lib/fairy/share/vfile.rb +183 -0
- data/lib/fairy/version.rb +8 -0
- data/lib/fairy.rb +206 -0
- data/sample/grep.rb +46 -0
- data/sample/ping.rb +19 -0
- data/sample/sort.rb +102 -0
- data/sample/wordcount.rb +61 -0
- data/spec/README +12 -0
- data/spec/fairy1_spec.rb +31 -0
- data/spec/fairy2_spec.rb +42 -0
- data/spec/fairy3_spec.rb +126 -0
- data/spec/fairy4_spec.rb +63 -0
- data/spec/fairy5_spec.rb +45 -0
- data/spec/fairy6_spec.rb +52 -0
- data/spec/fairy7_spec.rb +58 -0
- data/spec/fairy8_spec.rb +48 -0
- data/spec/mkdat.rb +148 -0
- data/spec/run_all.sh +65 -0
- data/test/testc.rb +7111 -0
- data/tools/cap_recipe/Capfile +144 -0
- data/tools/cap_recipe/cluster.yml.sample +14 -0
- data/tools/fairy_perf_graph.rb +444 -0
- data/tools/git-tag +44 -0
- data/tools/log-analysis.rb +62 -0
- data/tools/svn-ls-diff +38 -0
- data/tools/svn-tags +37 -0
- metadata +298 -0
@@ -0,0 +1,144 @@
|
|
1
|
+
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
cluster = YAML.load_file(File.dirname(__FILE__) + "/cluster.yml")
|
5
|
+
|
6
|
+
server cluster["master"], :master
|
7
|
+
cluster["nodes"].each{|node| server node, :nodes }
|
8
|
+
|
9
|
+
if cluster["run_uid"]
|
10
|
+
set :user, cluster["run_uid"]
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
desc <<-DESC
|
15
|
+
Start fairy on a cluster specified by cluster.yml
|
16
|
+
DESC
|
17
|
+
task :start do
|
18
|
+
start_master
|
19
|
+
sleep(3)
|
20
|
+
start_nodes
|
21
|
+
sleep(3)
|
22
|
+
ps
|
23
|
+
end
|
24
|
+
|
25
|
+
task :start_master, :roles => [:master] do
|
26
|
+
run %{ mkdir -p /tmp/fairy/tmpbuf }
|
27
|
+
run %{ nohup fairy master > /tmp/fairy_master.log 2>&1 & }
|
28
|
+
end
|
29
|
+
|
30
|
+
task :start_nodes, :roles => [:nodes] do
|
31
|
+
run %{ mkdir -p /tmp/fairy/tmpbuf }
|
32
|
+
run %{ nohup fairy node > /tmp/fairy_node.log 2>&1 & }
|
33
|
+
end
|
34
|
+
|
35
|
+
desc <<-DESC
|
36
|
+
Show fairy processes on the cluster.
|
37
|
+
DESC
|
38
|
+
task :ps, :roles => [:master, :nodes] do
|
39
|
+
run %{ ps -ef | grep -E 'fairy (master|controller|node|processor) ' | grep -v 'grep' | tee /tmp/fairy_ps }
|
40
|
+
end
|
41
|
+
|
42
|
+
desc <<-DESC
|
43
|
+
Stop fairy.
|
44
|
+
DESC
|
45
|
+
task :stop, :roles => [:master, :nodes] do
|
46
|
+
ps
|
47
|
+
run %{ if [ -s /tmp/fairy_ps ]; then cat /tmp/fairy_ps | awk '{print $2}' | xargs kill -9; fi }
|
48
|
+
end
|
49
|
+
|
50
|
+
desc <<-DESC
|
51
|
+
Restart fairy.
|
52
|
+
DESC
|
53
|
+
task :restart do
|
54
|
+
stop
|
55
|
+
start
|
56
|
+
end
|
57
|
+
|
58
|
+
desc <<-DESC
|
59
|
+
Show some information of the cluster.
|
60
|
+
* Versions of Ruby and fairy.
|
61
|
+
* Values of $RUBYLIB/$FAIRY_HOME environment var.
|
62
|
+
* Path of fairy command.
|
63
|
+
DESC
|
64
|
+
task :showinfo, :roles => [:master, :nodes] do
|
65
|
+
run %{ ruby -v }
|
66
|
+
run %{ echo "RUBYLIB=$RUBYLIB" }
|
67
|
+
run %{ echo "FAIRY_HOME=$FAIRY_HOME" }
|
68
|
+
run %{ grep 'Version =' "$FAIRY_HOME"/lib/fairy/version.rb | sed -e 's/^ *//' }
|
69
|
+
run %{ which -a fairy }
|
70
|
+
end
|
71
|
+
|
72
|
+
desc <<-DESC
|
73
|
+
Truncate fairy log (master:/tmp/fairy/log).
|
74
|
+
DESC
|
75
|
+
task :clear_log, :roles => [:master] do
|
76
|
+
run %{ cat /dev/null > /tmp/fairy/log }
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
desc <<-DESC
|
83
|
+
Install fairy to all hosts in the cluster.
|
84
|
+
You need to grant "sudo" privilege to "run_uid" user.
|
85
|
+
If you put the following files in "cap_recipe" directory
|
86
|
+
before the execution, they will be distributed to the
|
87
|
+
cluster.
|
88
|
+
* fairy.conf -> $FAIRY_HOME/etc
|
89
|
+
* .fairyenv -> "run_uid" user's home directory
|
90
|
+
This task also creates "/tmp/fairy/tmpbuf" directory
|
91
|
+
on all hosts.
|
92
|
+
DESC
|
93
|
+
task :install, :roles => [:master, :nodes] do
|
94
|
+
pkg = "fairy"
|
95
|
+
if local_pkg
|
96
|
+
pkg = "/tmp/#{File.basename(local_pkg)}"
|
97
|
+
upload "#{local_pkg}", "#{pkg}"
|
98
|
+
end
|
99
|
+
|
100
|
+
if gem_cmd
|
101
|
+
sudo %{ #{gem_cmd} install #{pkg} }
|
102
|
+
else
|
103
|
+
sudo %{ gem install #{pkg} }
|
104
|
+
end
|
105
|
+
|
106
|
+
run %{ mkdir -p /tmp/fairy/tmpbuf }
|
107
|
+
|
108
|
+
if FileTest.exists? "fairy.conf"
|
109
|
+
sync_conf
|
110
|
+
end
|
111
|
+
|
112
|
+
if FileTest.exists? ".fairyenv"
|
113
|
+
sync_env
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
set :fairy_home, ENV['FAIRY_HOME'] unless exists? :fairy_home
|
118
|
+
|
119
|
+
task :sync_conf, :roles => [:master, :nodes] do
|
120
|
+
unless fairy_home
|
121
|
+
raise "fairy_home is not specified."
|
122
|
+
end
|
123
|
+
upload "fairy.conf", "/tmp/fairy.conf"
|
124
|
+
sudo %{ cp "/tmp/fairy.conf" #{fairy_home}/etc }
|
125
|
+
run %{ rm /tmp/fairy.conf }
|
126
|
+
end
|
127
|
+
|
128
|
+
task :sync_env, :roles => [:master, :nodes] do
|
129
|
+
upload ".fairyenv", ".fairyenv"
|
130
|
+
end
|
131
|
+
|
132
|
+
desc <<-DESC
|
133
|
+
Update fairy on all hosts in the cluster.
|
134
|
+
You need to grant "sudo" privilege to "run_uid" user.
|
135
|
+
DESC
|
136
|
+
task :update, :roles => [:master, :nodes] do
|
137
|
+
if gem_cmd
|
138
|
+
sudo %{ #{gem_cmd} update fairy }
|
139
|
+
else
|
140
|
+
sudo %{ gem update fairy }
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
|
@@ -0,0 +1,444 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Log Analysis Data Visualizer for fairy
|
4
|
+
#
|
5
|
+
# This tool processes a output of log-analysis.rb
|
6
|
+
# and outputs a PNG image.
|
7
|
+
#
|
8
|
+
# by Hajime Masuda (Rakuten, Inc.)
|
9
|
+
#
|
10
|
+
# encoding: UTF-8
|
11
|
+
|
12
|
+
require 'time'
|
13
|
+
#require 'pp'
|
14
|
+
|
15
|
+
require 'rubygems'
|
16
|
+
require 'cairo'
|
17
|
+
|
18
|
+
|
19
|
+
module FairyPerformanceGraph
|
20
|
+
|
21
|
+
class Base
|
22
|
+
attr_reader :parent, :margin_top, :margin_button, :margin_between_elems, :margin_left, :margin_right
|
23
|
+
|
24
|
+
def initialize(parent)
|
25
|
+
@parent = parent
|
26
|
+
|
27
|
+
@margin_top = 0
|
28
|
+
@margin_button = 0
|
29
|
+
@margin_between_elems = 0
|
30
|
+
@margin_left = 0
|
31
|
+
@margin_right = 0
|
32
|
+
end
|
33
|
+
|
34
|
+
def height
|
35
|
+
init_val = @margin_top + @margin_button - @margin_between_elems
|
36
|
+
@contents.inject(init_val){|result,elem|
|
37
|
+
result += elem.height + @margin_between_elems
|
38
|
+
result
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Session < Base
|
44
|
+
attr_accessor :nodes, :scale, :start_at, :elapsed
|
45
|
+
|
46
|
+
def initialize(width=1200)
|
47
|
+
super(nil)
|
48
|
+
|
49
|
+
@contents = []
|
50
|
+
|
51
|
+
@margin_top = 25
|
52
|
+
@margin_button = 5
|
53
|
+
@margin_between_elems = 5
|
54
|
+
@margin_left = 5
|
55
|
+
@margin_right = 5
|
56
|
+
|
57
|
+
@width = width
|
58
|
+
@bgcolor = "WHITE"
|
59
|
+
@fgcolor = "BLACK"
|
60
|
+
@rounded_r = 10
|
61
|
+
@font_size = 18.0
|
62
|
+
|
63
|
+
@cache = {}
|
64
|
+
end
|
65
|
+
|
66
|
+
def index(name)
|
67
|
+
return @cache[name] if @cache[name]
|
68
|
+
|
69
|
+
idx = @contents.index{|node| node.name == name}
|
70
|
+
@cache[name] = idx if idx
|
71
|
+
|
72
|
+
return idx
|
73
|
+
end
|
74
|
+
|
75
|
+
def nodes
|
76
|
+
@contents
|
77
|
+
end
|
78
|
+
|
79
|
+
def max_child_width
|
80
|
+
@width - (@margin_left + @margin_right)
|
81
|
+
end
|
82
|
+
|
83
|
+
def draw(write_to)
|
84
|
+
@start_at = Time.now
|
85
|
+
@end_at = Time.at(0)
|
86
|
+
|
87
|
+
@contents.each{|node|
|
88
|
+
node.processors.each{|processor|
|
89
|
+
processor.filters.each{|filter|
|
90
|
+
@start_at = filter.start_at if @start_at > filter.start_at
|
91
|
+
@end_at = filter.end_at if @end_at < filter.end_at
|
92
|
+
}
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
max_filter_width = @contents[0].processors[0].filters[0].max_width
|
97
|
+
@scale = max_filter_width.to_f / (@end_at - @start_at)
|
98
|
+
|
99
|
+
@elapsed = @end_at - @start_at
|
100
|
+
|
101
|
+
format = Cairo::FORMAT_ARGB32
|
102
|
+
surface = Cairo::ImageSurface.new(format, @width, (h = height))
|
103
|
+
@context = Cairo::Context.new(surface)
|
104
|
+
|
105
|
+
# draw background
|
106
|
+
@context.set_source_color(@bgcolor)
|
107
|
+
@context.rectangle(0, 0, @width, h)
|
108
|
+
@context.fill
|
109
|
+
|
110
|
+
# draw nodes
|
111
|
+
height_total = 0
|
112
|
+
w = @width - (@margin_left + @margin_right)
|
113
|
+
@contents.each_with_index{|node,i|
|
114
|
+
off_y = @margin_top + (i * @margin_between_elems) + height_total
|
115
|
+
height_total += node.draw(@context, @margin_left, off_y, w)
|
116
|
+
}
|
117
|
+
|
118
|
+
|
119
|
+
# draw time lines
|
120
|
+
ml = @margin_left + @contents[0].margin_left + @contents[0].processors[0].margin_left
|
121
|
+
interval = max_filter_width / 20
|
122
|
+
y = h - @margin_button
|
123
|
+
@context.set_source_color("DARK_KHAKI")
|
124
|
+
@context.set_line_width(0.5)
|
125
|
+
@context.set_dash([2,2])
|
126
|
+
21.times{|i|
|
127
|
+
@context.move_to(ml, @margin_top)
|
128
|
+
@context.line_to(ml, y)
|
129
|
+
@context.stroke
|
130
|
+
ml += interval
|
131
|
+
}
|
132
|
+
@context.set_dash(nil)
|
133
|
+
|
134
|
+
# draw text
|
135
|
+
@context.set_source_color(@fgcolor)
|
136
|
+
@context.move_to(5, @font_size)
|
137
|
+
@context.set_font_size(@font_size)
|
138
|
+
@context.show_text("START:#{@start_at} -- END:#{@end_at} (#{@elapsed.to_i} sec.)")
|
139
|
+
|
140
|
+
# output image
|
141
|
+
surface.write_to_png(write_to)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
class Node < Base
|
146
|
+
attr_reader :name, :line_color, :bgcolor
|
147
|
+
|
148
|
+
def initialize(parent, name)
|
149
|
+
super(parent)
|
150
|
+
|
151
|
+
@name = name
|
152
|
+
@contents = []
|
153
|
+
|
154
|
+
@margin_left = 80
|
155
|
+
|
156
|
+
@bgcolor = "#FFFFDD"
|
157
|
+
@fgcolor = "BLACK"
|
158
|
+
@rounded_r = 10
|
159
|
+
@font_size = 14.0
|
160
|
+
@line_width = 0.5
|
161
|
+
end
|
162
|
+
|
163
|
+
def processors
|
164
|
+
@contents
|
165
|
+
end
|
166
|
+
|
167
|
+
def max_child_width
|
168
|
+
@parent.max_child_width - (@margin_left + @margin_right)
|
169
|
+
end
|
170
|
+
|
171
|
+
def draw(context, x, y, width)
|
172
|
+
context.set_source_color(@bgcolor)
|
173
|
+
context.rounded_rectangle(x, y, width, (h = height), @rounded_r, @rounded_r)
|
174
|
+
context.fill_preserve
|
175
|
+
context.set_source_color(@fgcolor)
|
176
|
+
context.set_line_width(@line_width)
|
177
|
+
context.stroke
|
178
|
+
|
179
|
+
context.move_to((x + 5), (y + @font_size + 5))
|
180
|
+
context.set_font_size(@font_size)
|
181
|
+
context.show_text(@name)
|
182
|
+
|
183
|
+
# draw processors
|
184
|
+
height_total = 0
|
185
|
+
off_x = x + @margin_left
|
186
|
+
w = width - @margin_left
|
187
|
+
@contents.each_with_index{|processor,i|
|
188
|
+
off_y = y + @margin_top + (i * @margin_between_elems) + height_total
|
189
|
+
height_total += processor.draw(context, off_x, off_y, w)
|
190
|
+
}
|
191
|
+
|
192
|
+
h
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
class Processor < Base
|
197
|
+
attr_reader :id
|
198
|
+
|
199
|
+
def initialize(parent, id)
|
200
|
+
super(parent)
|
201
|
+
|
202
|
+
@id = id
|
203
|
+
@contents = []
|
204
|
+
|
205
|
+
@margin_top = 10
|
206
|
+
@margin_button = 10
|
207
|
+
@margin_between_elems = 5
|
208
|
+
@margin_left = 40
|
209
|
+
@margin_right = 5
|
210
|
+
|
211
|
+
@fgcolor = "DARK_BLUE"
|
212
|
+
@line_width = 0.75
|
213
|
+
@dash = [5,2]
|
214
|
+
@font_size = 14.0
|
215
|
+
end
|
216
|
+
|
217
|
+
def filters
|
218
|
+
@contents
|
219
|
+
end
|
220
|
+
|
221
|
+
def max_child_width
|
222
|
+
@parent.max_child_width - (@margin_left + @margin_right)
|
223
|
+
end
|
224
|
+
|
225
|
+
def draw(context, x, y, width)
|
226
|
+
context.set_source_color(@fgcolor)
|
227
|
+
context.set_font_size(@font_size)
|
228
|
+
context.move_to((x + 5), (y + @font_size + 5))
|
229
|
+
context.show_text("P#"+id.to_s)
|
230
|
+
|
231
|
+
@contents.sort!{|a,b| a.start_at - b.start_at}
|
232
|
+
|
233
|
+
# draw filters
|
234
|
+
height_total = 0
|
235
|
+
off_x = x + @margin_left
|
236
|
+
@contents.each_with_index{|filter,i|
|
237
|
+
off_y = y + @margin_top + (i * @margin_between_elems) + height_total
|
238
|
+
height_total += filter.draw(context, off_x, off_y)
|
239
|
+
}
|
240
|
+
|
241
|
+
if @id == (@parent.processors.size - 1)
|
242
|
+
h = height
|
243
|
+
else
|
244
|
+
context.set_source_color(@fgcolor)
|
245
|
+
context.set_line_width(@line_width)
|
246
|
+
context.set_dash(@dash)
|
247
|
+
context.move_to(x, (y + (h = height)))
|
248
|
+
context.line_to((x + width), (y + h))
|
249
|
+
context.stroke
|
250
|
+
context.set_dash(nil)
|
251
|
+
end
|
252
|
+
|
253
|
+
h
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
class Filter < Base
|
258
|
+
IMPORT = 0
|
259
|
+
PROCESSING = 1
|
260
|
+
EXPORT = 2
|
261
|
+
|
262
|
+
TYPE_NAME2IDX = {
|
263
|
+
"IMPORT" => IMPORT,
|
264
|
+
"PROCESSING" => PROCESSING,
|
265
|
+
"EXPORT" => EXPORT
|
266
|
+
}
|
267
|
+
|
268
|
+
PTN_NAME = /\A(?:\w+::)*\w+\[((\d+)-\d+)(\[(\d+:\d+)\])?\]\z/
|
269
|
+
|
270
|
+
attr_reader :name, :type, :start_at, :end_at, :elapsed, :job_id, :task_id, :key_info
|
271
|
+
attr_accessor :elapsed_for_store
|
272
|
+
|
273
|
+
def initialize(parent, name, type, start_at, end_at, elapsed)
|
274
|
+
super(parent)
|
275
|
+
|
276
|
+
@name = name
|
277
|
+
@type = TYPE_NAME2IDX[type]
|
278
|
+
@start_at = Time.parse(start_at)
|
279
|
+
@end_at = Time.parse(end_at)
|
280
|
+
@elapsed = elapsed
|
281
|
+
|
282
|
+
@fgcolor = "BLACK"
|
283
|
+
@font_size = 8
|
284
|
+
|
285
|
+
@job_id, @task_id, @key_info = self.class.parse_name(@name)
|
286
|
+
end
|
287
|
+
|
288
|
+
def self.parse_name(name)
|
289
|
+
if m = name.match(PTN_NAME)
|
290
|
+
m[1..3]
|
291
|
+
else
|
292
|
+
[]
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def height
|
297
|
+
10
|
298
|
+
end
|
299
|
+
|
300
|
+
def max_width
|
301
|
+
@parent.max_child_width
|
302
|
+
end
|
303
|
+
|
304
|
+
def draw(context, x, y)
|
305
|
+
case @type
|
306
|
+
when IMPORT
|
307
|
+
context.set_source_color("LIGHT_BLUE")
|
308
|
+
when PROCESSING
|
309
|
+
context.set_source_color("PINK")
|
310
|
+
when EXPORT
|
311
|
+
context.set_source_color("LIGHT_GREEN")
|
312
|
+
end
|
313
|
+
|
314
|
+
scale = @parent.parent.parent.scale
|
315
|
+
global_start_at = @parent.parent.parent.start_at
|
316
|
+
|
317
|
+
width = ((@end_at - @start_at) * scale).to_i
|
318
|
+
off_x = x + ((@start_at - global_start_at) * scale).to_i
|
319
|
+
|
320
|
+
context.rectangle(off_x, y, width, (h = height))
|
321
|
+
context.fill
|
322
|
+
|
323
|
+
if @type == IMPORT
|
324
|
+
context.set_source_color("CORNFLOWER_BLUE")
|
325
|
+
width_store = (@elapsed_for_store * scale).to_i
|
326
|
+
off_x_store = off_x + width - width_store
|
327
|
+
context.rectangle(off_x_store, y, width_store, h)
|
328
|
+
context.fill
|
329
|
+
end
|
330
|
+
|
331
|
+
context.set_source_color(@fgcolor)
|
332
|
+
context.move_to((off_x + 2), (y + @font_size))
|
333
|
+
context.set_font_size(@font_size)
|
334
|
+
context.show_text(@name)
|
335
|
+
|
336
|
+
global_elaped = @parent.parent.parent.elapsed
|
337
|
+
percentage = (@elapsed / global_elaped) * 100
|
338
|
+
|
339
|
+
context.set_source_color("RED")
|
340
|
+
context.move_to(off_x + 2 + ((@name.size * @font_size) * 0.7).to_i, (y + @font_size))
|
341
|
+
context.set_font_size(@font_size + 2.0)
|
342
|
+
context.show_text("%.1f sec. (%.1f%%)" % [@elapsed, percentage])
|
343
|
+
|
344
|
+
|
345
|
+
h
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
class LogParser
|
350
|
+
PTN_SEPARATOR = /, */
|
351
|
+
PTN_FIRST_RECORD = /\A([^ ]+) \[P\]#(\d+) (.+)\z/
|
352
|
+
|
353
|
+
attr_reader :io
|
354
|
+
|
355
|
+
def initialize(log)
|
356
|
+
@io = File.open(log, "r")
|
357
|
+
end
|
358
|
+
|
359
|
+
def each_log
|
360
|
+
@io.each_with_index{|ln,i|
|
361
|
+
ln.chomp!
|
362
|
+
ary = ln.split(PTN_SEPARATOR)
|
363
|
+
m = ary[0].match(PTN_FIRST_RECORD)
|
364
|
+
|
365
|
+
line_no = i+1
|
366
|
+
host_name = m[1]
|
367
|
+
processor_id = m[2].to_i
|
368
|
+
filter_name = m[3]
|
369
|
+
type = ary[1]
|
370
|
+
start_at = ary[2]
|
371
|
+
end_at = ary[3]
|
372
|
+
elasped = ary[4].to_f
|
373
|
+
|
374
|
+
yield(line_no, host_name, processor_id, filter_name, type, start_at, end_at, elasped)
|
375
|
+
}
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
#
|
380
|
+
# main
|
381
|
+
#
|
382
|
+
def run(input_from, output_to, width=nil)
|
383
|
+
if width
|
384
|
+
graph = Session.new(width)
|
385
|
+
else
|
386
|
+
graph = Session.new
|
387
|
+
end
|
388
|
+
|
389
|
+
log = LogParser.new(input_from)
|
390
|
+
log.each_log{|line_no, host_name, processor_id, filter_name, type, start_at, end_at, elapsed|
|
391
|
+
|
392
|
+
if type == "STORE"
|
393
|
+
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
|
396
|
+
filter.elapsed_for_store = elapsed
|
397
|
+
#$stderr.puts("set filter.elapsed_for_store (#{filter.name})")
|
398
|
+
next
|
399
|
+
end
|
400
|
+
|
401
|
+
if idx = graph.index(host_name)
|
402
|
+
node = graph.nodes[idx]
|
403
|
+
else
|
404
|
+
node = Node.new(graph, host_name)
|
405
|
+
graph.nodes << node
|
406
|
+
end
|
407
|
+
|
408
|
+
unless processor = node.processors[processor_id]
|
409
|
+
processor = Processor.new(node, processor_id)
|
410
|
+
node.processors << processor
|
411
|
+
end
|
412
|
+
|
413
|
+
processor.filters << Filter.new(processor, filter_name, type, start_at, end_at, elapsed)
|
414
|
+
}
|
415
|
+
#pp graph
|
416
|
+
|
417
|
+
graph.draw(output_to)
|
418
|
+
end
|
419
|
+
module_function :run
|
420
|
+
|
421
|
+
end
|
422
|
+
|
423
|
+
|
424
|
+
#
|
425
|
+
# boot strap
|
426
|
+
#
|
427
|
+
unless ARGV.size == 2 || ARGV.size == 3
|
428
|
+
$stderr.puts "Usage: #{File.basename($0)} INPUT_FROM OUTPUT_TO [IMAGE_WIDTH]"
|
429
|
+
exit(1)
|
430
|
+
end
|
431
|
+
|
432
|
+
INPUT_FROM = ARGV.shift
|
433
|
+
OUTPUT_TO = ARGV.shift
|
434
|
+
WIDTH = ARGV.shift
|
435
|
+
|
436
|
+
if WIDTH
|
437
|
+
FairyPerformanceGraph.run(INPUT_FROM, OUTPUT_TO, WIDTH.to_i)
|
438
|
+
else
|
439
|
+
FairyPerformanceGraph.run(INPUT_FROM, OUTPUT_TO)
|
440
|
+
end
|
441
|
+
|
442
|
+
exit(0)
|
443
|
+
|
444
|
+
|