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 CHANGED
@@ -3,13 +3,16 @@ script = <<EOS
3
3
  #!/bin/sh
4
4
  RUBY_CMD="%RUBY_CMD%"
5
5
 
6
- $RUBY_CMD -e "require 'theine/client'" "$@"
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
- f.write(script.gsub("%RUBY_CMD%", ARGV[0]))
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
- argv = ARGV.dup
63
- ARGV.clear
65
+ module Theine
66
+ class Client
67
+ def self.start
68
+ new
69
+ end
64
70
 
65
- DRb.start_service
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
- i = 1
68
- begin
69
- balancer = DRbObject.new_with_uri("druby://localhost:#{PRAILS_BASE_PORT}")
70
- sleep 0.1 until port = balancer.get_port
71
- prails = DRbObject.new_with_uri("druby://localhost:#{port}")
72
- rescue DRb::DRbConnError
73
- sleep 0.5
74
- putc "."
75
- i += 1
76
- retry
77
- end
78
- putc "\n"
79
-
80
- trap('INT') {
81
- %x[kill -2 #{prails.pid}]
82
- }
83
-
84
- prails.stdin = IOUndumpedProxy.new($stdin)
85
- prails.stdout = IOUndumpedProxy.new($stdout)
86
- prails.stderr = IOUndumpedProxy.new($stderr)
87
- begin
88
- prails.command_rails(argv)
89
- rescue DRb::DRbConnError
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
- PRAILS_BASE_PORT = 11000
5
- PRAILS_MAX_PORT = 11100
6
- MIN_FREE_INSTANCES = 2
7
- RAILS_APP_ROOT = Dir.pwd
6
+ module Theine
7
+ class Server
8
+ include DRb::DRbUndumped
9
+ attr_reader :config
8
10
 
9
- class PrailsServer
10
- include DRb::DRbUndumped
11
+ def initialize
12
+ @config = ConfigReader.new(Dir.pwd)
11
13
 
12
- def initialize
13
- @instances = []
14
- @spawning = []
14
+ @workers = []
15
+ @spawning = []
15
16
 
16
- @available_ports = ((PRAILS_BASE_PORT + 1)..PRAILS_MAX_PORT).to_a
17
- @check_mutex = Mutex.new
18
- @instances_mutex = Mutex.new
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
- def instance_boot(port)
30
- puts "+ instance #{port}"
21
+ run
22
+ end
31
23
 
32
- @instances_mutex.synchronize do
33
- @spawning.pop
34
- @instances << port
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
- def get_port
39
- add_instance if all_size == 0
32
+ def worker_boot(port)
33
+ puts "+ worker #{port}"
40
34
 
41
- port = nil
42
- while port.nil? && all_size > 0
43
- @instances_mutex.synchronize do
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
- Thread.new { check_min_free_instances }
41
+ def worker_done(port)
42
+ puts "- worker #{port}"
43
+ end
50
44
 
51
- port
52
- end
45
+ def get_port
46
+ add_worker if all_size == 0
53
47
 
54
- def check_min_free_instances
55
- if @check_mutex.try_lock
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
- @check_mutex.unlock
51
+
52
+ Thread.new { check_min_free_workers }
53
+
54
+ port
63
55
  end
64
- end
65
56
 
66
- def all_size
67
- @instances_mutex.synchronize { @instances.size + @spawning.size }
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 = PrailsServer.new
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.2
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/instance.rb
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
@@ -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