expedite 0.2.0 → 0.2.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.
- checksums.yaml +4 -4
- data/lib/expedite/client/base.rb +165 -0
- data/lib/expedite/client/exec.rb +7 -10
- data/lib/expedite/client/invoke.rb +5 -150
- data/lib/expedite/server/agent.rb +2 -0
- data/lib/expedite/version.rb +1 -1
- metadata +7 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6a83d8e6d3ba40d5e35466b011f32538004da8cf4c680cbf7b7b8d492110d46b
|
|
4
|
+
data.tar.gz: 8cbdb627f4c2cf7468be1d478fe3811a974a95a6ac429ace89bfea14ad09090a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 875af703feeeae36c5f0986a6049dbd0867182298446aa6d9a6a1e2bce1ecc1914b7be8acfa367c6a140cb77743b1654fb7088c3913c8573f38e45cb21e6865b
|
|
7
|
+
data.tar.gz: 589627e2e61a4dc7fef263da86ff44f24ac31b32f301063595f3d0ba979ff579ccd5d54365ecca608ce2c2dd7e4cd88aeda089573189500c0da495d84bf778f5
|
|
@@ -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
|
data/lib/expedite/client/exec.rb
CHANGED
|
@@ -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 <
|
|
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/
|
|
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
|
-
|
|
11
|
-
|
|
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)
|
data/lib/expedite/version.rb
CHANGED
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.
|
|
4
|
+
version: 0.2.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bing-Chang Lai
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-12-
|
|
11
|
+
date: 2022-12-21 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
|
|
@@ -49,7 +50,7 @@ homepage: https://rubygems.org/gems/expedite
|
|
|
49
50
|
licenses:
|
|
50
51
|
- MIT
|
|
51
52
|
metadata: {}
|
|
52
|
-
post_install_message:
|
|
53
|
+
post_install_message:
|
|
53
54
|
rdoc_options: []
|
|
54
55
|
require_paths:
|
|
55
56
|
- lib
|
|
@@ -64,8 +65,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
64
65
|
- !ruby/object:Gem::Version
|
|
65
66
|
version: '0'
|
|
66
67
|
requirements: []
|
|
67
|
-
rubygems_version: 3.
|
|
68
|
-
signing_key:
|
|
68
|
+
rubygems_version: 3.3.26
|
|
69
|
+
signing_key:
|
|
69
70
|
specification_version: 4
|
|
70
71
|
summary: Expedite startup of Ruby process
|
|
71
72
|
test_files: []
|