theine 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/theine_set_ruby +5 -2
- data/lib/theine/client.rb +91 -28
- data/lib/theine/config.rb +27 -0
- data/lib/theine/server.rb +61 -52
- data/lib/theine/worker.rb +124 -0
- metadata +3 -2
- data/lib/theine/instance.rb +0 -114
data/bin/theine_set_ruby
CHANGED
@@ -3,13 +3,16 @@ script = <<EOS
|
|
3
3
|
#!/bin/sh
|
4
4
|
RUBY_CMD="%RUBY_CMD%"
|
5
5
|
|
6
|
-
$RUBY_CMD -e "require '
|
6
|
+
$RUBY_CMD -e "require '%CLIENT_RB_PATH%'" "$@"
|
7
7
|
EOS
|
8
8
|
|
9
9
|
if ARGV[0]
|
10
10
|
path = %x[which theine]
|
11
|
+
client_rb_path = File.expand_path('../../lib/theine/client.rb', __FILE__)
|
11
12
|
File.open(path.strip, "w") do |f|
|
12
|
-
|
13
|
+
script.gsub!("%CLIENT_RB_PATH%", client_rb_path)
|
14
|
+
script.gsub!("%RUBY_CMD%", ARGV[0])
|
15
|
+
f.write(script)
|
13
16
|
end
|
14
17
|
puts "Set theine to run using #{ARGV[0]}."
|
15
18
|
else
|
data/lib/theine/client.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'drb/drb'
|
2
2
|
require 'readline'
|
3
|
-
|
4
|
-
PRAILS_BASE_PORT = 11000
|
3
|
+
require_relative './config'
|
5
4
|
|
6
5
|
class IOUndumpedProxy
|
7
6
|
include DRb::DRbUndumped
|
@@ -31,6 +30,10 @@ class IOUndumpedProxy
|
|
31
30
|
end
|
32
31
|
end
|
33
32
|
|
33
|
+
def gets(*args)
|
34
|
+
@obj.gets(*args)
|
35
|
+
end
|
36
|
+
|
34
37
|
def puts(*lines)
|
35
38
|
@obj.puts(*lines)
|
36
39
|
end
|
@@ -59,32 +62,92 @@ class IOUndumpedProxy
|
|
59
62
|
end
|
60
63
|
end
|
61
64
|
|
62
|
-
|
63
|
-
|
65
|
+
module Theine
|
66
|
+
class Client
|
67
|
+
def self.start
|
68
|
+
new
|
69
|
+
end
|
64
70
|
|
65
|
-
|
71
|
+
attr_reader :config
|
72
|
+
|
73
|
+
def initialize
|
74
|
+
@config = ConfigReader.new(Dir.pwd)
|
75
|
+
reset_argv!
|
76
|
+
trap_signals
|
77
|
+
begin
|
78
|
+
connect_worker
|
79
|
+
redirect_io
|
80
|
+
run_command
|
81
|
+
ensure
|
82
|
+
stop
|
83
|
+
end
|
84
|
+
end
|
66
85
|
|
67
|
-
|
68
|
-
begin
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
end
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
86
|
+
def stop(sleep_for = 0.1)
|
87
|
+
begin
|
88
|
+
if @worker
|
89
|
+
%x[kill -2 #{@worker.pid}] # TODO: if client was term-ed, term worker (maybe term)
|
90
|
+
puts "Stopping Theine worker."
|
91
|
+
sleep(sleep_for) if sleep_for > 0 # to finish receiving IO
|
92
|
+
end
|
93
|
+
rescue DRb::DRbConnError
|
94
|
+
end
|
95
|
+
exit(0)
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
def run_command
|
100
|
+
@worker.command_rails(@argv)
|
101
|
+
rescue DRb::DRbConnError
|
102
|
+
$stderr.puts "\nTheine closed the connection."
|
103
|
+
end
|
104
|
+
|
105
|
+
def reset_argv!
|
106
|
+
@argv = ARGV.dup
|
107
|
+
ARGV.clear
|
108
|
+
end
|
109
|
+
|
110
|
+
def trap_signals
|
111
|
+
trap('INT') { exit(0) } # TODO: is this needed?
|
112
|
+
trap('TERM') { exit(0) }
|
113
|
+
end
|
114
|
+
|
115
|
+
def redirect_io
|
116
|
+
@worker.stdin = IOUndumpedProxy.new($stdin)
|
117
|
+
@worker.stdout = IOUndumpedProxy.new($stdout)
|
118
|
+
@worker.stderr = IOUndumpedProxy.new($stderr)
|
119
|
+
end
|
120
|
+
|
121
|
+
def connect_worker
|
122
|
+
balancer = wait_until_result("Cannot connect to theine server. Waiting") do
|
123
|
+
object = DRbObject.new_with_uri("druby://localhost:#{config.base_port}")
|
124
|
+
object.respond_to?(:get_port) # test if connected
|
125
|
+
object
|
126
|
+
end
|
127
|
+
port = wait_until_result("Waiting for Theine worker...") do
|
128
|
+
balancer.get_port
|
129
|
+
end
|
130
|
+
@worker = DRbObject.new_with_uri("druby://localhost:#{port}")
|
131
|
+
end
|
132
|
+
|
133
|
+
WaitResultNoResultError = Class.new(StandardError)
|
134
|
+
def wait_until_result(wait_message)
|
135
|
+
result = nil
|
136
|
+
dots = 0
|
137
|
+
begin
|
138
|
+
result = yield
|
139
|
+
raise WaitResultNoResultError unless result
|
140
|
+
rescue DRb::DRbConnError, WaitResultNoResultError
|
141
|
+
print dots == 0 ? wait_message : "."
|
142
|
+
dots += 1
|
143
|
+
sleep 0.5
|
144
|
+
retry
|
145
|
+
end
|
146
|
+
print "\n" if dots > 0
|
147
|
+
result
|
148
|
+
end
|
149
|
+
end
|
90
150
|
end
|
151
|
+
|
152
|
+
DRb.start_service
|
153
|
+
Theine::Client.start
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Theine
|
4
|
+
class ConfigReader
|
5
|
+
attr_reader :rails_root
|
6
|
+
attr_accessor :base_port, :max_port, :min_free_workers, :spawn_parallel
|
7
|
+
def initialize(rails_root)
|
8
|
+
@rails_root = rails_root
|
9
|
+
@base_port = 11000
|
10
|
+
@max_port = 11100
|
11
|
+
@min_free_workers = 2
|
12
|
+
@spawn_parallel = true
|
13
|
+
load_config(File.expand_path("~/.theine"))
|
14
|
+
load_config("#{rails_root}/.theine")
|
15
|
+
end
|
16
|
+
|
17
|
+
def load_config(path)
|
18
|
+
if File.exist?(path)
|
19
|
+
config = YAML.load(File.read(path))
|
20
|
+
config.each_pair do |k, v|
|
21
|
+
setter = :"#{k}="
|
22
|
+
send(setter, v) if respond_to?(setter)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/theine/server.rb
CHANGED
@@ -1,75 +1,84 @@
|
|
1
1
|
require 'drb/drb'
|
2
2
|
require 'thread'
|
3
|
+
require 'yaml'
|
4
|
+
require_relative './config'
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
module Theine
|
7
|
+
class Server
|
8
|
+
include DRb::DRbUndumped
|
9
|
+
attr_reader :config
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
+
def initialize
|
12
|
+
@config = ConfigReader.new(Dir.pwd)
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
@spawning = []
|
14
|
+
@workers = []
|
15
|
+
@spawning = []
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
def add_instance
|
22
|
-
path = File.expand_path('../instance.rb', __FILE__)
|
23
|
-
port = @available_ports.shift
|
24
|
-
puts "(spawn #{port})"
|
25
|
-
spawn("ruby", path, PRAILS_BASE_PORT.to_s, port.to_s, RAILS_APP_ROOT)
|
26
|
-
@instances_mutex.synchronize { @spawning << 1 }
|
27
|
-
end
|
17
|
+
@available_ports = ((config.base_port + 1)..config.max_port).to_a
|
18
|
+
@check_mutex = Mutex.new
|
19
|
+
@workers_mutex = Mutex.new
|
28
20
|
|
29
|
-
|
30
|
-
|
21
|
+
run
|
22
|
+
end
|
31
23
|
|
32
|
-
|
33
|
-
|
34
|
-
|
24
|
+
def add_worker
|
25
|
+
path = File.expand_path('../worker.rb', __FILE__)
|
26
|
+
port = @available_ports.shift
|
27
|
+
puts "(spawn #{port})"
|
28
|
+
spawn("ruby", path, config.base_port.to_s, port.to_s, config.rails_root)
|
29
|
+
@workers_mutex.synchronize { @spawning << 1 }
|
35
30
|
end
|
36
|
-
end
|
37
31
|
|
38
|
-
|
39
|
-
|
32
|
+
def worker_boot(port)
|
33
|
+
puts "+ worker #{port}"
|
40
34
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
port = @instances.shift
|
35
|
+
@workers_mutex.synchronize do
|
36
|
+
@spawning.pop
|
37
|
+
@workers << port
|
45
38
|
end
|
46
|
-
sleep 0.1 unless port
|
47
39
|
end
|
48
40
|
|
49
|
-
|
41
|
+
def worker_done(port)
|
42
|
+
puts "- worker #{port}"
|
43
|
+
end
|
50
44
|
|
51
|
-
|
52
|
-
|
45
|
+
def get_port
|
46
|
+
add_worker if all_size == 0
|
53
47
|
|
54
|
-
|
55
|
-
|
56
|
-
# TODO: mutex, and dont do it if already in progress
|
57
|
-
# do this in thread
|
58
|
-
while all_size < MIN_FREE_INSTANCES
|
59
|
-
sleep 0.1 until @instances_mutex.synchronize { @spawning.empty? }
|
60
|
-
add_instance
|
48
|
+
port = @workers_mutex.synchronize do
|
49
|
+
@workers.shift
|
61
50
|
end
|
62
|
-
|
51
|
+
|
52
|
+
Thread.new { check_min_free_workers }
|
53
|
+
|
54
|
+
port
|
63
55
|
end
|
64
|
-
end
|
65
56
|
|
66
|
-
|
67
|
-
|
57
|
+
def check_min_free_workers
|
58
|
+
if @check_mutex.try_lock
|
59
|
+
# TODO: mutex, and dont do it if already in progress
|
60
|
+
# do this in thread
|
61
|
+
while all_size < config.min_free_workers
|
62
|
+
unless config.spawn_parallel
|
63
|
+
sleep 0.1 until @workers_mutex.synchronize { @spawning.empty? }
|
64
|
+
end
|
65
|
+
add_worker
|
66
|
+
end
|
67
|
+
@check_mutex.unlock
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def all_size
|
72
|
+
@workers_mutex.synchronize { @workers.size + @spawning.size }
|
73
|
+
end
|
74
|
+
private
|
75
|
+
def run
|
76
|
+
DRb.start_service("druby://localhost:#{config.base_port}", self)
|
77
|
+
check_min_free_workers
|
78
|
+
DRb.thread.join
|
79
|
+
end
|
68
80
|
end
|
69
81
|
end
|
70
82
|
|
71
|
-
server =
|
72
|
-
DRb.start_service("druby://localhost:#{PRAILS_BASE_PORT}", server)
|
83
|
+
server = Theine::Server.new
|
73
84
|
|
74
|
-
server.check_min_free_instances
|
75
|
-
DRb.thread.join
|
@@ -0,0 +1,124 @@
|
|
1
|
+
root_path = ARGV[2]
|
2
|
+
APP_PATH = "#{root_path}/config/application"
|
3
|
+
require "#{root_path}/config/boot"
|
4
|
+
require "#{root_path}/config/environment"
|
5
|
+
require 'drb/drb'
|
6
|
+
|
7
|
+
$real_stdout = $stdout
|
8
|
+
$real_stderr = $stderr
|
9
|
+
|
10
|
+
module Theine
|
11
|
+
class Worker
|
12
|
+
attr_reader :stdin, :stdout, :stderr
|
13
|
+
InputProxy = Struct.new :input do
|
14
|
+
# Reads a line from the input
|
15
|
+
def readline(prompt)
|
16
|
+
case readline_arity
|
17
|
+
when 1 then input.readline(prompt)
|
18
|
+
else input.readline
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def completion_proc=(val)
|
23
|
+
input.completion_proc = val
|
24
|
+
end
|
25
|
+
|
26
|
+
def readline_arity
|
27
|
+
input.method_missing(:method, :readline).arity
|
28
|
+
rescue NameError
|
29
|
+
0
|
30
|
+
end
|
31
|
+
|
32
|
+
def gets(*args)
|
33
|
+
input.gets(*args)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize
|
38
|
+
@pumps = []
|
39
|
+
end
|
40
|
+
|
41
|
+
def command_rails(argv)
|
42
|
+
rails_reload!
|
43
|
+
|
44
|
+
ARGV.clear
|
45
|
+
ARGV.concat(argv)
|
46
|
+
|
47
|
+
require 'pry'
|
48
|
+
::Rails.application.config.console = Pry
|
49
|
+
|
50
|
+
Pry.config.input = stdin
|
51
|
+
Pry.config.output = stdout
|
52
|
+
|
53
|
+
require 'rails/commands'
|
54
|
+
sleep 0.1 # allow Pumps to finish
|
55
|
+
DRb.stop_service
|
56
|
+
end
|
57
|
+
|
58
|
+
def stdin=(value)
|
59
|
+
@stdin = InputProxy.new(value)
|
60
|
+
$stdin = @stdin
|
61
|
+
end
|
62
|
+
|
63
|
+
def stdout=(value)
|
64
|
+
$orig_stdout = $stdout
|
65
|
+
@stdout = value
|
66
|
+
r, w = IO.pipe
|
67
|
+
$stdout = w
|
68
|
+
@pumps << Pump.new(r, @stdout)
|
69
|
+
end
|
70
|
+
|
71
|
+
def stderr=(value)
|
72
|
+
$orig_stderr = $stderr
|
73
|
+
@stderr = value
|
74
|
+
r, w = IO.pipe
|
75
|
+
$stderr = w
|
76
|
+
@pumps << Pump.new(r, @stderr)
|
77
|
+
end
|
78
|
+
|
79
|
+
def pid
|
80
|
+
::Process.pid
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
def rails_reload!
|
85
|
+
ActionDispatch::Reloader.cleanup!
|
86
|
+
ActionDispatch::Reloader.prepare!
|
87
|
+
end
|
88
|
+
|
89
|
+
class Pump < Thread
|
90
|
+
def initialize(input, output)
|
91
|
+
if output
|
92
|
+
@input = input
|
93
|
+
@output = output
|
94
|
+
super(&method(:main))
|
95
|
+
else
|
96
|
+
close_stream(input)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
def main
|
102
|
+
while buf = @input.sysread(1024)
|
103
|
+
@output.print(buf)
|
104
|
+
@output.flush
|
105
|
+
end
|
106
|
+
ensure
|
107
|
+
@output.close
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
base_port = ARGV[0]
|
114
|
+
worker_port = ARGV[1]
|
115
|
+
DRb.start_service("druby://localhost:#{worker_port}", Theine::Worker.new)
|
116
|
+
|
117
|
+
balancer = DRbObject.new_with_uri("druby://localhost:#{base_port}")
|
118
|
+
balancer.worker_boot(worker_port)
|
119
|
+
|
120
|
+
begin
|
121
|
+
DRb.thread.join
|
122
|
+
ensure
|
123
|
+
balancer.worker_done(worker_port)
|
124
|
+
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: theine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.3
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Jan Berdajs
|
@@ -23,7 +23,8 @@ files:
|
|
23
23
|
- lib/theine.rb
|
24
24
|
- lib/theine/client.rb
|
25
25
|
- lib/theine/server.rb
|
26
|
-
- lib/theine/
|
26
|
+
- lib/theine/worker.rb
|
27
|
+
- lib/theine/config.rb
|
27
28
|
- bin/theine
|
28
29
|
- bin/theine_server
|
29
30
|
- bin/theine_set_ruby
|
data/lib/theine/instance.rb
DELETED
@@ -1,114 +0,0 @@
|
|
1
|
-
root_path = ARGV[2]
|
2
|
-
APP_PATH = "#{root_path}/config/application"
|
3
|
-
require "#{root_path}/config/boot"
|
4
|
-
require "#{root_path}/config/environment"
|
5
|
-
require 'drb/drb'
|
6
|
-
|
7
|
-
$real_stdout = $stdout
|
8
|
-
$real_stderr = $stderr
|
9
|
-
|
10
|
-
class PrailsServer
|
11
|
-
attr_reader :stdin, :stdout, :stderr
|
12
|
-
InputProxy = Struct.new :input do
|
13
|
-
# Reads a line from the input
|
14
|
-
def readline(prompt)
|
15
|
-
case readline_arity
|
16
|
-
when 1 then input.readline(prompt)
|
17
|
-
else input.readline
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def completion_proc=(val)
|
22
|
-
input.completion_proc = val
|
23
|
-
end
|
24
|
-
|
25
|
-
def readline_arity
|
26
|
-
input.method_missing(:method, :readline).arity
|
27
|
-
rescue NameError
|
28
|
-
0
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def initialize
|
33
|
-
@pumps = []
|
34
|
-
end
|
35
|
-
|
36
|
-
def command_rails(argv)
|
37
|
-
rails_reload!
|
38
|
-
|
39
|
-
ARGV.clear
|
40
|
-
ARGV.concat(argv)
|
41
|
-
|
42
|
-
require 'pry'
|
43
|
-
::Rails.application.config.console = Pry
|
44
|
-
|
45
|
-
Pry.config.input = stdin
|
46
|
-
Pry.config.output = stdout
|
47
|
-
|
48
|
-
require 'rails/commands'
|
49
|
-
sleep 0.1 # allow Pumps to finish
|
50
|
-
DRb.stop_service
|
51
|
-
end
|
52
|
-
|
53
|
-
def stdin=(value)
|
54
|
-
@stdin = InputProxy.new(value)
|
55
|
-
$stdin = @stdin
|
56
|
-
end
|
57
|
-
|
58
|
-
def stdout=(value)
|
59
|
-
$orig_stdout = $stdout
|
60
|
-
@stdout = value
|
61
|
-
r, w = IO.pipe
|
62
|
-
$stdout = w
|
63
|
-
@pumps << Pump.new(r, @stdout)
|
64
|
-
end
|
65
|
-
|
66
|
-
def stderr=(value)
|
67
|
-
$orig_stderr = $stderr
|
68
|
-
@stderr = value
|
69
|
-
r, w = IO.pipe
|
70
|
-
$stderr = w
|
71
|
-
@pumps << Pump.new(r, @stderr)
|
72
|
-
end
|
73
|
-
|
74
|
-
def pid
|
75
|
-
::Process.pid
|
76
|
-
end
|
77
|
-
|
78
|
-
private
|
79
|
-
def rails_reload!
|
80
|
-
ActionDispatch::Reloader.cleanup!
|
81
|
-
ActionDispatch::Reloader.prepare!
|
82
|
-
end
|
83
|
-
|
84
|
-
class Pump < Thread
|
85
|
-
def initialize(input, output)
|
86
|
-
if output
|
87
|
-
@input = input
|
88
|
-
@output = output
|
89
|
-
super(&method(:main))
|
90
|
-
else
|
91
|
-
close_stream(input)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
private
|
96
|
-
def main
|
97
|
-
while buf = @input.sysread(1024)
|
98
|
-
@output.print(buf)
|
99
|
-
@output.flush
|
100
|
-
end
|
101
|
-
ensure
|
102
|
-
@output.close
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
base_port = ARGV[0]
|
108
|
-
instance_port = ARGV[1]
|
109
|
-
DRb.start_service("druby://localhost:#{instance_port}", PrailsServer.new)
|
110
|
-
|
111
|
-
balancer = DRbObject.new_with_uri("druby://localhost:#{base_port}")
|
112
|
-
balancer.instance_boot(instance_port)
|
113
|
-
|
114
|
-
DRb.thread.join
|