theine 0.0.2 → 0.0.3

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