actory 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/Gemfile +6 -0
  4. data/LICENSE +202 -0
  5. data/README.md +361 -0
  6. data/actory.gemspec +25 -0
  7. data/bin/actory-receiver +22 -0
  8. data/config/global.yml +2 -0
  9. data/config/receiver.yml.example +8 -0
  10. data/config/sender.yml.example +14 -0
  11. data/example/fibonacci.rb +28 -0
  12. data/example/fibonacci_parallel_in_processes.rb +39 -0
  13. data/example/fibonacci_parallel_in_threads.rb +39 -0
  14. data/example/fibonacci_single.rb +39 -0
  15. data/example/lib/benchmark.rb +11 -0
  16. data/example/pi.rb +28 -0
  17. data/example/pi_parallel_in_processes.rb +39 -0
  18. data/example/pi_parallel_in_threads.rb +39 -0
  19. data/example/pi_single.rb +39 -0
  20. data/example/prime.rb +28 -0
  21. data/example/prime_parallel_in_processes.rb +39 -0
  22. data/example/prime_parallel_in_threads.rb +39 -0
  23. data/example/prime_single.rb +39 -0
  24. data/lib/actory.rb +66 -0
  25. data/lib/actory/error.rb +7 -0
  26. data/lib/actory/errors/generator.rb +13 -0
  27. data/lib/actory/receiver.rb +17 -0
  28. data/lib/actory/receiver/event_handler.rb +17 -0
  29. data/lib/actory/receiver/plugin/auth.rb +15 -0
  30. data/lib/actory/receiver/plugin/example_fibonacci.rb +17 -0
  31. data/lib/actory/receiver/plugin/example_pi.rb +20 -0
  32. data/lib/actory/receiver/plugin/example_prime.rb +18 -0
  33. data/lib/actory/receiver/plugin/processor_count.rb +15 -0
  34. data/lib/actory/receiver/plugin/reload.rb +16 -0
  35. data/lib/actory/receiver/plugin/system_info.rb +31 -0
  36. data/lib/actory/receiver/worker.rb +43 -0
  37. data/lib/actory/sender.rb +14 -0
  38. data/lib/actory/sender/dispatcher.rb +172 -0
  39. data/log/.keep +0 -0
  40. metadata +195 -0
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'parallel'
4
+ require 'progressbar'
5
+
6
+ METHOD = "prime"
7
+ ARGS = (1..1000).to_a
8
+
9
+ require_relative "../lib/actory/receiver/plugin/example_#{METHOD}"
10
+ require_relative './lib/benchmark'
11
+
12
+ res = []
13
+ processor_count = Parallel.processor_count
14
+ pbar = ProgressBar.new(METHOD, processor_count)
15
+
16
+ ret, time = Benchmark.measure do
17
+ begin
18
+ plugin = Actory::Receiver::Plugin.new
19
+ res << Parallel.map(ARGS, :in_threads => processor_count) do |arg|
20
+ begin
21
+ pbar.set pbar.current + 1 if pbar.current <= processor_count
22
+ rescue
23
+ end
24
+ plugin.send(METHOD, arg)
25
+ end
26
+ res.each do |v|
27
+ puts "returned #{v}"
28
+ end
29
+ rescue => e
30
+ @num == nil ? @num = 0 : @num += 1
31
+ puts e
32
+ puts $@
33
+ sleep 1
34
+ retry if @num < 2
35
+ end
36
+ end
37
+
38
+ puts " => #{ret}"
39
+ puts " => time = #{time} sec"
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'parallel'
4
+ require 'progressbar'
5
+
6
+ METHOD = "prime"
7
+ ARGS = (1..1000).to_a
8
+
9
+ require_relative "../lib/actory/receiver/plugin/example_#{METHOD}"
10
+ require_relative './lib/benchmark'
11
+
12
+ res = []
13
+ processor_count = Parallel.processor_count
14
+ pbar = ProgressBar.new(METHOD, ARGS.count)
15
+
16
+ ret, time = Benchmark.measure do
17
+ begin
18
+ plugin = Actory::Receiver::Plugin.new
19
+ res << ARGS.map do |arg|
20
+ begin
21
+ pbar.set pbar.current + 1 if pbar.current <= ARGS.count
22
+ rescue
23
+ end
24
+ plugin.send(METHOD, arg)
25
+ end
26
+ res.each do |v|
27
+ puts "returned #{v}"
28
+ end
29
+ rescue => e
30
+ @num == nil ? @num = 0 : @num += 1
31
+ puts e
32
+ puts $@
33
+ sleep 1
34
+ retry if @num < 2
35
+ end
36
+ end
37
+
38
+ puts " => #{ret}"
39
+ puts " => time = #{time} sec"
@@ -0,0 +1,66 @@
1
+ require 'yaml'
2
+
3
+ module Actory
4
+
5
+ class Base
6
+ GLOBAL = YAML.load_file(File.expand_path("../../config/global.yml", __FILE__))
7
+
8
+ def self.get_logger_output(type="stdout", target=nil)
9
+ return nil unless type
10
+ out = nil
11
+ file = File.open(target, "a") if %w[file both].include?(type)
12
+ case type.downcase
13
+ when "stdout"
14
+ out = STDOUT
15
+ when "file"
16
+ out = file if target
17
+ when "both"
18
+ out = MultiIO.new(STDOUT, file) if file
19
+ else
20
+ out = STDOUT
21
+ end
22
+ out
23
+ end
24
+
25
+ def self.get_logger_level(level="fatal")
26
+ return nil unless level
27
+ logger_level = nil
28
+ case level.downcase
29
+ when "fatal"
30
+ logger_level = Logger::FATAL
31
+ when "error"
32
+ logger_level = Logger::ERROR
33
+ when "warn"
34
+ logger_level = Logger::WARN
35
+ when "info"
36
+ logger_level = Logger::INFO
37
+ when "debug"
38
+ logger_level = Logger::DEBUG
39
+ else
40
+ logger_level = Logger::FATAL
41
+ end
42
+ logger_level
43
+ end
44
+ end
45
+
46
+ class MultiIO
47
+ def initialize(*targets)
48
+ @targets = targets
49
+ end
50
+
51
+ def write(*args)
52
+ @targets.each {|t| t.write(*args)}
53
+ end
54
+
55
+ def close
56
+ @targets.each(&:close)
57
+ end
58
+ end
59
+
60
+ end #Actory
61
+
62
+ require 'rubygems'
63
+ require 'msgpack/rpc'
64
+ require 'parallel'
65
+ require 'logger'
66
+ Dir[File.join(File.dirname(__FILE__), "actory/*.rb")].each { |f| require_relative f }
@@ -0,0 +1,7 @@
1
+ module Actory
2
+ module Errors
3
+ end
4
+ end
5
+
6
+ require 'json'
7
+ Dir[File.join(File.dirname(__FILE__), "errors/*.rb")].each { |f| require_relative f }
@@ -0,0 +1,13 @@
1
+ module Actory
2
+ module Errors
3
+
4
+ class Generator
5
+
6
+ def json(level: nil, message: nil, backtrace: nil)
7
+ JSON.generate({:level => level.upcase, :message => message, :backtrace => backtrace})
8
+ end
9
+
10
+ end
11
+
12
+ end #Errors
13
+ end #Actory
@@ -0,0 +1,17 @@
1
+ module Actory
2
+ module Receiver
3
+
4
+ class Base < Actory::Base
5
+ RECEIVER = YAML.load_file(File.expand_path(File.join(GLOBAL['config'], "/receiver.yml"), __FILE__))
6
+ @@logger = Logger.new(get_logger_output(RECEIVER['log']['type'], RECEIVER['log']['target']))
7
+ @@logger.level = get_logger_level(RECEIVER['log']['level'])
8
+ end
9
+
10
+ end #Receiver
11
+ end #Actory
12
+
13
+ require 'msgpack/rpc/transport/udp'
14
+ require 'facter'
15
+ Dir[File.join(File.dirname(__FILE__), "receiver/*.rb")].each { |f| require_relative f }
16
+ Dir[File.join(File.dirname(__FILE__), "receiver/plugin/*.rb")].each { |f| require_relative f }
17
+
@@ -0,0 +1,17 @@
1
+ module Actory
2
+ module Receiver
3
+
4
+ class EventHandler < Base
5
+
6
+ def receive(method, arg=nil, results=[])
7
+ plugin = Actory::Receiver::Plugin.new
8
+ arg ? results << plugin.send(method, arg) : results << plugin.send(method)
9
+ return results
10
+ rescue => e
11
+ raise StandardError, e
12
+ end
13
+
14
+ end
15
+
16
+ end #Receiver
17
+ end #Actory
@@ -0,0 +1,15 @@
1
+ module Actory
2
+ module Receiver
3
+
4
+ class Plugin < Base
5
+
6
+ def auth?(shared_key)
7
+ RECEIVER['shared_key'] == shared_key
8
+ rescue => e
9
+ msg = Actory::Errors::Generator.new.json(level: "ERROR", message: e.message, backtrace: $@)
10
+ raise StandardError, msg
11
+ end
12
+ end
13
+
14
+ end #Receiver
15
+ end #Actory
@@ -0,0 +1,17 @@
1
+ module Actory
2
+ module Receiver
3
+
4
+ class Plugin < Base
5
+
6
+ def fibonacci(num=10)
7
+ raise StandardError unless [Fixnum, String].include?(num.class)
8
+ num = num.to_i if num.class == String and num =~ /\A[0-9]+\z/
9
+ num <= 1 ? num : fibonacci(num - 1) + fibonacci(num - 2)
10
+ rescue => e
11
+ msg = Actory::Errors::Generator.new.json(level: "ERROR", message: e.message, backtrace: $@)
12
+ raise StandardError, msg
13
+ end
14
+ end
15
+
16
+ end #Receiver
17
+ end #Actory
@@ -0,0 +1,20 @@
1
+ module Actory
2
+ module Receiver
3
+
4
+ class Plugin < Base
5
+
6
+ def pi(len=2)
7
+ raise StandardError unless [Fixnum, String].include?(len.class)
8
+ len = len.to_i if len.class == String and len =~ /\A[0-9]+\z/
9
+ b = 10 ** len
10
+ b2 = b << 1
11
+ pi = (len * 8 + 1).step(3, -2).inject(b) {|a, i| (i >> 1) * (a + b2) / i} - b
12
+ return "3.#{pi}"
13
+ rescue => e
14
+ msg = Actory::Errors::Generator.new.json(level: "ERROR", message: e.message, backtrace: $@)
15
+ raise StandardError, msg
16
+ end
17
+ end
18
+
19
+ end #Receiver
20
+ end #Actory
@@ -0,0 +1,18 @@
1
+ module Actory
2
+ module Receiver
3
+
4
+ class Plugin < Base
5
+ require 'prime'
6
+
7
+ def prime(num=1)
8
+ raise StandardError unless [Fixnum, String].include?(num.class)
9
+ num = num.to_i if num.class == String and num =~ /\A[0-9]+\z/
10
+ Prime.each(num).to_a
11
+ rescue => e
12
+ msg = Actory::Errors::Generator.new.json(level: "ERROR", message: e.message, backtrace: $@)
13
+ raise StandardError, msg
14
+ end
15
+ end
16
+
17
+ end #Receiver
18
+ end #Actory
@@ -0,0 +1,15 @@
1
+ module Actory
2
+ module Receiver
3
+
4
+ class Plugin < Base
5
+
6
+ def processor_count
7
+ return Parallel.processor_count
8
+ rescue => e
9
+ msg = Actory::Errors::Generator.new.json(level: "ERROR", message: e.message, backtrace: $@)
10
+ raise StandardError, msg
11
+ end
12
+ end
13
+
14
+ end #Receiver
15
+ end #Actory
@@ -0,0 +1,16 @@
1
+ module Actory
2
+ module Receiver
3
+
4
+ class Plugin < Base
5
+
6
+ def reload
7
+ Dir[File.join(File.dirname(__FILE__), "./*.rb")].each { |f| load f }
8
+ rescue => e
9
+ msg = Actory::Errors::Generator.new.json(level: "ERROR", message: e.message, backtrace: $@)
10
+ raise StandardError, e
11
+ end
12
+
13
+ end
14
+
15
+ end #Receiver
16
+ end #Actory
@@ -0,0 +1,31 @@
1
+ module Actory
2
+ module Receiver
3
+
4
+ class Plugin < Base
5
+
6
+ def system_info
7
+ info = {
8
+ :processor => {
9
+ :count => Facter.value(:processorcount),
10
+ :physicalcount => Facter.value(:physicalprocessorcount),
11
+ },
12
+ :memory => {
13
+ :free => Facter.value(:memoryfree),
14
+ :size => Facter.value(:memorysize),
15
+ },
16
+ :swap => {
17
+ :free => Facter.value(:swapfree),
18
+ :size => Facter.value(:swapsize),
19
+ },
20
+ :timezone => Facter.value(:timezone),
21
+ }
22
+ return info
23
+ rescue => e
24
+ msg = Actory::Errors::Generator.new.json(level: "ERROR", message: e.message, backtrace: $@)
25
+ raise StandardError, msg
26
+ end
27
+
28
+ end
29
+
30
+ end #Receiver
31
+ end #Actory
@@ -0,0 +1,43 @@
1
+ module Actory
2
+ module Receiver
3
+
4
+ class Worker < Base
5
+ def initialize(protocol="tcp", target: nil)
6
+ protocol = RECEIVER['protocol'] if RECEIVER['protocol']
7
+ num = Parallel.processor_count
8
+ target ||= Actory::Receiver::EventHandler
9
+ Parallel.map(0..num, :in_processes => num) do |n|
10
+ @@logger.info "Starting Actory Receiver Worker ##{n + 1}/#{num} (PID = #{Process.pid}, PGROUP = #{Process.getpgrp}, protocol = #{protocol})"
11
+ is_retried = false
12
+ begin
13
+ worker = send(protocol, target, n)
14
+ Signal.trap(:TERM) { worker.stop }
15
+ Signal.trap(:INT) { worker.stop }
16
+ worker.run
17
+ rescue => e
18
+ @@logger.error(Actory::Errors::Generator.new.json(level: "error", message: e, backtrace: $@)) unless is_retried
19
+ is_retried = true
20
+ retry
21
+ end
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def tcp(target, num)
28
+ worker = MessagePack::RPC::Server.new
29
+ worker.listen(RECEIVER['address'], RECEIVER['port'] + num, target.new)
30
+ worker
31
+ end
32
+
33
+ def udp(target, num)
34
+ address = MessagePack::RPC::Address.new(RECEIVER['address'], RECEIVER['port'] + num)
35
+ listener = MessagePack::RPC::UDPServerTransport.new(address)
36
+ worker = MessagePack::RPC::Server.new
37
+ worker.listen(listener, target.new)
38
+ worker
39
+ end
40
+ end
41
+
42
+ end #Receiver
43
+ end #Actory
@@ -0,0 +1,14 @@
1
+ module Actory
2
+ module Sender
3
+
4
+ class Base < Actory::Base
5
+ SENDER = YAML.load_file(File.expand_path(File.join(GLOBAL['config'], "sender.yml"), __FILE__))
6
+ @@logger = Logger.new(get_logger_output(SENDER['log']['type'], SENDER['log']['target']))
7
+ @@logger.level = get_logger_level(SENDER['log']['level'])
8
+ end
9
+
10
+ end #Sender
11
+ end #Actory
12
+
13
+ require 'progressbar'
14
+ Dir[File.join(File.dirname(__FILE__), "sender/*.rb")].each { |f| require_relative f }
@@ -0,0 +1,172 @@
1
+ module Actory
2
+ module Sender
3
+
4
+ class Dispatcher < Base
5
+ attr_accessor :actors, :trusted_hosts, :system_info, :receiver_count, :my_processor_count
6
+
7
+ def initialize(actors: [])
8
+ @actors = []
9
+ @trusted_hosts = []
10
+ @receiver_count = 0
11
+ @system_info = []
12
+ @my_processor_count = Parallel.processor_count
13
+ ret = initial_handshaking(actors)
14
+ raise StandardError if ret == 0
15
+ count = establish_connections
16
+ raise StandardError if count == 0
17
+ rescue => e
18
+ @@logger.error Actory::Errors::Generator.new.json(level: "error", message: "Initialization failed.", backtrace: $@)
19
+ exit 1
20
+ end
21
+
22
+ def message(method, args=[], results=[])
23
+ args = [nil] if args.empty?
24
+ assignment = assign_jobs(args)
25
+
26
+ pbar = ProgressBar.new(method, @receiver_count) if SENDER['show_progress']
27
+
28
+ results << Parallel.map(assignment, :in_processes => @receiver_count) do |arg, actor|
29
+ if SENDER['show_progress']
30
+ begin
31
+ pbar.set pbar.current + 1 if pbar.current <= @receiver_count
32
+ rescue
33
+ end
34
+ end
35
+
36
+ begin
37
+ actor.send("receive", "reload") if SENDER['reload_receiver_plugins']
38
+ res = actor.send("receive", method, arg)
39
+ sleep SENDER['get_interval']
40
+ ret = res.get
41
+ ret.flatten!
42
+ {actor.address.to_s => ret}
43
+ rescue => e
44
+ @@logger.warn Actory::Errors::Generator.new.json(level: "warn", message: "Something wrong with sending a message to #{actor.address}", backtrace: $@)
45
+ actor = change_actor(actor)
46
+ retry
47
+ end
48
+ end
49
+ results.flatten
50
+ end
51
+
52
+ private
53
+
54
+ def initial_handshaking(actors=[])
55
+ actors = SENDER['actors'] if actors.empty? and SENDER['actors'].nil? == false
56
+ actors.each do |actor|
57
+ next unless actor.class == String
58
+ actor = actor.gsub(/:/, " ").split
59
+ host = actor[0]
60
+ port = actor[1].to_i
61
+ @cli = MessagePack::RPC::Client.new(host, port)
62
+ @cli.timeout = SENDER['auth']['timeout']
63
+ ret = get_trusted_hosts(host)
64
+ next unless ret
65
+ @system_info << {:host => host, :system_info => get_system_info}
66
+ get_receiver_count
67
+ end
68
+ rescue => e
69
+ puts $@, e
70
+ end
71
+
72
+ def establish_connections
73
+ case SENDER['policy']
74
+ when "even"
75
+ establish_connections_evenly
76
+ when "random"
77
+ establish_connections_randomly(@receiver_count)
78
+ when "safe-random"
79
+ return 0 if @trusted_hosts.empty?
80
+ establish_connections_randomly(@my_processor_count / @trusted_hosts.count)
81
+ else
82
+ establish_connections_evenly
83
+ end
84
+ @actors.count
85
+ end
86
+
87
+ def establish_connections_randomly(num=0)
88
+ establish_connections_helper(num)
89
+ end
90
+
91
+ def establish_connections_evenly
92
+ return nil if @trusted_hosts.empty?
93
+ cores_per_host = @my_processor_count / @trusted_hosts.count
94
+ cores_per_host = 1 if cores_per_host <= 0
95
+ establish_connections_helper(cores_per_host)
96
+ end
97
+
98
+ def establish_connections_helper(num=0)
99
+ num.times do |n|
100
+ SENDER['actors'].each do |actor|
101
+ next unless actor.class == String
102
+ actor = actor.gsub(/:/, " ").split
103
+ host = actor[0]
104
+ next unless trusted_hosts.include?(host)
105
+ port = actor[1].to_i
106
+ cli = MessagePack::RPC::Client.new(host, port + n)
107
+ cli.timeout = SENDER['timeout']
108
+ @actors << cli
109
+ end
110
+ end
111
+ @@logger.debug @actors
112
+ end
113
+
114
+ def get_trusted_hosts(host)
115
+ res = @cli.send("receive", "auth?", SENDER['auth']['shared_key'])
116
+ res.get[0] ? @trusted_hosts << host : nil
117
+ rescue => e
118
+ @@logger.warn Actory::Errors::Generator.new.json(level: "warn", message: "#{__method__} failed with #{host}", backtrace: $@)
119
+ return nil
120
+ end
121
+
122
+ def get_system_info
123
+ res = @cli.send("receive", "system_info")
124
+ res.get[0]
125
+ end
126
+
127
+ def get_receiver_count
128
+ res = @cli.send("receive", "processor_count")
129
+ @receiver_count += res.get[0] if res.get[0]
130
+ end
131
+
132
+ def assign_jobs(args)
133
+ num = 0
134
+ params = {}
135
+ actors = @actors.sample(@my_processor_count)
136
+ args.each do |arg|
137
+ next if params.has_key?(arg)
138
+ num = 0 unless actors[num]
139
+ actor = actors[num]
140
+ num += 1
141
+ params.merge!(arg => actor)
142
+ end
143
+ @@logger.debug params
144
+ params
145
+ end
146
+
147
+ #def select_actors
148
+ # actors = nil
149
+ # case SENDER['policy']
150
+ # when "even"
151
+ # actors = @actors
152
+ # when "random", "safe-random"
153
+ # actors = @actors.sample(@my_processor_count)
154
+ # else
155
+ # actors = @actors
156
+ # end
157
+ # actors
158
+ #end
159
+
160
+ def change_actor(previous_actor)
161
+ new_actor = nil
162
+ loop do
163
+ new_actor = @actors.sample
164
+ break unless new_actor == previous_actor
165
+ end
166
+ new_actor
167
+ end
168
+
169
+ end
170
+
171
+ end #Sender
172
+ end #Actory