actory 0.0.2

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 (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