expedite 0.2.0 → 0.2.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: da93957645e66819877d4c8638d2d42eb4880c631d6b4f8f224a3a9502c45a36
4
- data.tar.gz: 179e78a453744f0375f6b184ffa1a2cff321b7ab92deafc9be5297fb00c63c4b
3
+ metadata.gz: '0218f397835f80f748b0c255e8097aad9b8e8063122d15415a19f88e35546335'
4
+ data.tar.gz: 643e16d53333346a199595b2cb9fcab21116f53c606a505a40675d6ca074baaf
5
5
  SHA512:
6
- metadata.gz: 8cf3e2d403eab2d5cb7960a1d5cc43a4f94c3d0495ce36b360d32ec77ad7e5f9887a0e7aca183afa2e441ac3ff23a555ec6254f8441d971783376bdfc84db471
7
- data.tar.gz: 30f00102d8d8c6cc59f138841caa116421f3002871551dca20718dfc5100aec80f469e8bb31b1d1ee8158020852090b2c053bff5d5fd7c9c728fdc26e4c3c6d0
6
+ metadata.gz: c27ae0ac8775724ae0da4983e2f4ec04754e1cacd29bd632043c36f2c7634dd3914b92fc2e9d15f0b263418fdf0813e4d34aef7d4191fa0b14c368fed4993fb9
7
+ data.tar.gz: '09d8bdf813c976cffe6bfb3c2a6a31f14f1e0e1e6577a06a8e67e2be66271dc09a1180723bda57e1fa91a891f152f0316c5d893f795357d33ba090139782b6aa'
@@ -0,0 +1,165 @@
1
+ require 'socket'
2
+
3
+ require 'expedite/env'
4
+ require 'expedite/errors'
5
+ require 'expedite/protocol'
6
+
7
+ module Expedite
8
+ module Client
9
+ class Base
10
+ CONNECT_TIMEOUT = 1
11
+ BOOT_TIMEOUT = 20
12
+
13
+ attr_reader :args, :env, :agent
14
+ attr_reader :server
15
+
16
+ def initialize(env: nil, agent: nil)
17
+ @env = env || Env.new
18
+ @agent = agent
19
+
20
+ @server_booted = false
21
+ end
22
+
23
+ def call(*args)
24
+ begin
25
+ connect
26
+ rescue Errno::ENOENT, Errno::ECONNRESET, Errno::ECONNREFUSED
27
+ boot_server
28
+ connect
29
+ end
30
+
31
+ perform(*args)
32
+ ensure
33
+ server.close if server
34
+ end
35
+
36
+ def perform(*args)
37
+ verify_server_version
38
+
39
+ agent, client = UNIXSocket.pair
40
+ connect_to_agent(client, args)
41
+ run_command(client, agent, args)
42
+ end
43
+
44
+ def verify_server_version
45
+ server_version = server.gets.chomp
46
+ raise ArgumentError, "Server mismatch. Expected #{env.version}, got #{server_version}." if server_version != env.version
47
+ end
48
+
49
+ def connect_to_agent(client, args)
50
+ server.send_io client
51
+
52
+ server.send_object({"args" => args, "agent" => agent}, env)
53
+
54
+ if IO.select([server], [], [], CONNECT_TIMEOUT)
55
+ server.gets or raise CommandNotFound
56
+ else
57
+ raise "Error connecting to Expedite server"
58
+ end
59
+ end
60
+
61
+ def run_command(client, agent, args)
62
+ log "sending command"
63
+
64
+ agent.send_io STDOUT
65
+ agent.send_io STDERR
66
+ agent.send_io STDIN
67
+
68
+ agent.send_object({
69
+ "args" => args,
70
+ "env" => ENV.to_hash,
71
+ "method" => run_command_method,
72
+ }, env)
73
+
74
+ pid = server.gets
75
+ pid = pid.chomp if pid
76
+
77
+ # We must not close the client socket until we are sure that the application has
78
+ # received the FD. Otherwise the FD can end up getting closed while it's in the server
79
+ # socket buffer on OS X. This doesn't happen on Linux.
80
+ client.close
81
+
82
+ if pid && !pid.empty?
83
+ log "got pid: #{pid}"
84
+
85
+ ## suspend_resume_on_tstp_cont(pid)
86
+
87
+ ## forward_signals(application)
88
+ result = agent.recv_object
89
+ if result.key?("exception")
90
+ e = result["exception"]
91
+ log "got exception #{e}"
92
+ raise e
93
+ end
94
+
95
+ ret = result["return"]
96
+ log "got return value #{ret}"
97
+ return ret
98
+ else
99
+ log "got no pid"
100
+ raise UnknownError, "got no pid"
101
+ end
102
+ ensure
103
+ agent.close
104
+ end
105
+
106
+ def boot_server
107
+ env.socket_path.unlink if env.socket_path.exist?
108
+
109
+ pid = Process.spawn(gem_env, env.server_command, out: File::NULL)
110
+ timeout = Time.now + BOOT_TIMEOUT
111
+
112
+ @server_booted = true
113
+
114
+ until env.socket_path.exist?
115
+ _, status = Process.waitpid2(pid, Process::WNOHANG)
116
+
117
+ if status
118
+ # Server did not start
119
+ raise ArgumentError, "Server exited: #{status.exitstatus}"
120
+ elsif Time.now > timeout
121
+ $stderr.puts "Starting Expedite server with `#{env.server_command}` " \
122
+ "timed out after #{BOOT_TIMEOUT} seconds"
123
+ exit 1
124
+ end
125
+
126
+ sleep 0.1
127
+ end
128
+ end
129
+
130
+ def server_booted?
131
+ @server_booted
132
+ end
133
+
134
+ def stop_server
135
+ server.close
136
+ @server = nil
137
+ env.stop
138
+ end
139
+
140
+ def log(message)
141
+ env.log "[client] #{message}"
142
+ end
143
+
144
+ def connect
145
+ @server = UNIXSocket.open(env.socket_path)
146
+ end
147
+
148
+ def gem_env
149
+ bundle = Bundler.bundle_path.to_s
150
+ paths = Gem.path + ENV["GEM_PATH"].to_s.split(File::PATH_SEPARATOR)
151
+
152
+ {
153
+ "GEM_PATH" => [bundle, *paths].uniq.join(File::PATH_SEPARATOR),
154
+ "GEM_HOME" => bundle
155
+ }
156
+ end
157
+
158
+
159
+ protected
160
+ def run_command_method
161
+ raise NotImplementedError, "run_command_method must be overriden"
162
+ end
163
+ end
164
+ end
165
+ end
@@ -1,18 +1,10 @@
1
1
 
2
2
  # Based on https://github.com/rails/spring/blob/master/lib/spring/client/run.rb
3
-
4
- require 'bundler'
5
- require 'rbconfig'
6
- require 'socket'
7
-
8
- require 'expedite/client/invoke'
9
- require 'expedite/env'
10
- require 'expedite/errors'
11
- require 'expedite/protocol'
3
+ require 'expedite/client/base'
12
4
 
13
5
  module Expedite
14
6
  module Client
15
- class Exec < Invoke
7
+ class Exec < Base
16
8
  FORWARDED_SIGNALS = %w(INT QUIT USR1 USR2 INFO WINCH) & Signal.list.keys
17
9
 
18
10
  def initialize(env: nil, agent: nil)
@@ -128,6 +120,11 @@ module Expedite
128
120
  application.puts(sig)
129
121
  application.gets.to_i
130
122
  end
123
+
124
+ protected
125
+ def run_command_method
126
+ "exec"
127
+ end
131
128
  end
132
129
  end
133
130
  end
@@ -1,158 +1,13 @@
1
1
  require 'socket'
2
2
 
3
- require 'expedite/env'
4
- require 'expedite/errors'
5
- require 'expedite/protocol'
3
+ require 'expedite/client/base'
6
4
 
7
5
  module Expedite
8
6
  module Client
9
- class Invoke
10
- CONNECT_TIMEOUT = 1
11
- BOOT_TIMEOUT = 20
12
-
13
- attr_reader :args, :env, :agent
14
- attr_reader :server
15
-
16
- def initialize(env: nil, agent: nil)
17
- @env = env || Env.new
18
- @agent = agent
19
-
20
- @server_booted = false
21
- end
22
-
23
- def call(*args)
24
- begin
25
- connect
26
- rescue Errno::ENOENT, Errno::ECONNRESET, Errno::ECONNREFUSED
27
- boot_server
28
- connect
29
- end
30
-
31
- perform(*args)
32
- ensure
33
- server.close if server
34
- end
35
-
36
- def perform(*args)
37
- verify_server_version
38
-
39
- agent, client = UNIXSocket.pair
40
- connect_to_agent(client, args)
41
- run_command(client, agent, args)
42
- end
43
-
44
- def verify_server_version
45
- server_version = server.gets.chomp
46
- raise ArgumentError, "Server mismatch. Expected #{env.version}, got #{server_version}." if server_version != env.version
47
- end
48
-
49
- def connect_to_agent(client, args)
50
- server.send_io client
51
-
52
- server.send_object({"args" => args, "agent" => agent}, env)
53
-
54
- if IO.select([server], [], [], CONNECT_TIMEOUT)
55
- server.gets or raise CommandNotFound
56
- else
57
- raise "Error connecting to Expedite server"
58
- end
59
- end
60
-
61
- def run_command(client, agent, args)
62
- log "sending command"
63
-
64
- agent.send_io STDOUT
65
- agent.send_io STDERR
66
- agent.send_io STDIN
67
-
68
- agent.send_object({
69
- "args" => args,
70
- "env" => ENV.to_hash,
71
- "method" => "invoke"
72
- }, env)
73
-
74
- pid = server.gets
75
- pid = pid.chomp if pid
76
-
77
- # We must not close the client socket until we are sure that the application has
78
- # received the FD. Otherwise the FD can end up getting closed while it's in the server
79
- # socket buffer on OS X. This doesn't happen on Linux.
80
- client.close
81
-
82
- if pid && !pid.empty?
83
- log "got pid: #{pid}"
84
-
85
- ## suspend_resume_on_tstp_cont(pid)
86
-
87
- ## forward_signals(application)
88
- result = agent.recv_object
89
- if result.key?("exception")
90
- e = result["exception"]
91
- log "got exception #{e}"
92
- raise e
93
- end
94
-
95
- ret = result["return"]
96
- log "got return value #{ret}"
97
- return ret
98
- else
99
- log "got no pid"
100
- raise UnknownError, "got no pid"
101
- end
102
- ensure
103
- agent.close
104
- end
105
-
106
- def boot_server
107
- env.socket_path.unlink if env.socket_path.exist?
108
-
109
- pid = Process.spawn(gem_env, env.server_command, out: File::NULL)
110
- timeout = Time.now + BOOT_TIMEOUT
111
-
112
- @server_booted = true
113
-
114
- until env.socket_path.exist?
115
- _, status = Process.waitpid2(pid, Process::WNOHANG)
116
-
117
- if status
118
- # Server did not start
119
- raise ArgumentError, "Server exited: #{status.exitstatus}"
120
- elsif Time.now > timeout
121
- $stderr.puts "Starting Expedite server with `#{env.server_command}` " \
122
- "timed out after #{BOOT_TIMEOUT} seconds"
123
- exit 1
124
- end
125
-
126
- sleep 0.1
127
- end
128
- end
129
-
130
- def server_booted?
131
- @server_booted
132
- end
133
-
134
- def stop_server
135
- server.close
136
- @server = nil
137
- env.stop
138
- end
139
-
140
- def log(message)
141
- env.log "[client] #{message}"
142
- end
143
-
144
- def connect
145
- @server = UNIXSocket.open(env.socket_path)
146
- end
147
-
148
- def gem_env
149
- bundle = Bundler.bundle_path.to_s
150
- paths = Gem.path + ENV["GEM_PATH"].to_s.split(File::PATH_SEPARATOR)
151
-
152
- {
153
- "GEM_PATH" => [bundle, *paths].uniq.join(File::PATH_SEPARATOR),
154
- "GEM_HOME" => bundle
155
- }
7
+ class Invoke < Base
8
+ protected
9
+ def run_command_method
10
+ "invoke"
156
11
  end
157
12
  end
158
13
  end
@@ -141,6 +141,7 @@ module Expedite
141
141
  preload unless preloaded?
142
142
 
143
143
  args, env, method = client.recv_object.values_at("args", "env", "method")
144
+ log "serve #{args} using #{method}"
144
145
 
145
146
  exec_name = args.shift
146
147
  action = Expedite::Actions.lookup(exec_name)
@@ -149,6 +150,7 @@ module Expedite
149
150
  connect_database # why are we connecting prior? is this for invoke?
150
151
  pid = case method
151
152
  when "invoke"
153
+ # TODO: Invoke in a worker process instead of the preloader
152
154
  serve_invoke(client, action, args, env)
153
155
  else
154
156
  serve_fork(client, action, args, env)
@@ -1,3 +1,3 @@
1
1
  module Expedite
2
- VERSION = '0.2.0'
2
+ VERSION = '0.2.1'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: expedite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bing-Chang Lai
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-16 00:00:00.000000000 Z
11
+ date: 2022-12-17 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Manages Ruby processes that can be used to spawn child processes faster.
14
14
  email: johnny.lai@me.com
@@ -29,6 +29,7 @@ files:
29
29
  - lib/expedite/cli/server.rb
30
30
  - lib/expedite/cli/status.rb
31
31
  - lib/expedite/cli/stop.rb
32
+ - lib/expedite/client/base.rb
32
33
  - lib/expedite/client/exec.rb
33
34
  - lib/expedite/client/invoke.rb
34
35
  - lib/expedite/env.rb