zeus 0.2.0.beta2 → 0.2.0
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/README.md +15 -10
- data/lib/zeus/client.rb +1 -1
- data/lib/zeus/dsl.rb +4 -0
- data/lib/zeus/process.rb +8 -2
- data/lib/zeus/server.rb +2 -3
- data/lib/zeus/server/acceptor.rb +32 -4
- data/lib/zeus/server/acceptor_registration_monitor.rb +1 -5
- data/lib/zeus/server/client_handler.rb +29 -3
- data/lib/zeus/server/file_monitor.rb +0 -2
- data/lib/zeus/server/stage.rb +45 -15
- data/lib/zeus/ui.rb +5 -1
- data/lib/zeus/version.rb +1 -1
- metadata +5 -6
- data/ext/.DS_Store +0 -0
data/README.md
CHANGED
@@ -14,10 +14,15 @@ Because waiting 25 seconds sucks, but waiting 0.4 seconds doesn't.
|
|
14
14
|
|
15
15
|
Soon? You can use Zeus now, but don't expect it to be perfect. I'm working hard on it.
|
16
16
|
|
17
|
-
##
|
17
|
+
## Requirements
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
Pretty specific:
|
20
|
+
|
21
|
+
* OS X 10.7+
|
22
|
+
* Ruby 1.9+
|
23
|
+
* Backported GC from Ruby 2.0.
|
24
|
+
|
25
|
+
You can install the GC-patched ruby from [this gist](https://gist.github.com/1688857) or from RVM.
|
21
26
|
|
22
27
|
## Installation
|
23
28
|
|
@@ -52,22 +57,22 @@ Run some commands:
|
|
52
57
|
zeus rake -T
|
53
58
|
zeus runner omg.rb
|
54
59
|
|
60
|
+
|
55
61
|
## TODO (roughly prioritized)
|
56
62
|
|
57
|
-
* Make sure
|
58
|
-
*
|
59
|
-
*
|
60
|
-
* Handle connections for not-yet-started sockets
|
63
|
+
* Make sure that when a command process's connection is dropped, it is killed
|
64
|
+
* less leaky handling of at_exit pid killing
|
65
|
+
* Instead of exiting when requesting an as-yet-unbooted acceptor, wait until it's available then run.
|
61
66
|
* Refactor, refactor, refactor...
|
67
|
+
* Make sure client connection requests are handled immediately (Chunk the select loop)
|
68
|
+
* Don't fork to handshake client to acceptor
|
69
|
+
* Eliminate the client-side exit lag for zeus commands.
|
62
70
|
* Support other frameworks?
|
63
|
-
* Periodically re-attempt to add lost files to KQueue
|
64
71
|
* Figure out how to run full test suites without multiple env loads
|
65
|
-
* Don't replace a socket with changed deps until the new one is ready
|
66
72
|
|
67
73
|
## Ideas (not quite TODOs)
|
68
74
|
|
69
75
|
* (maybe) Start the preloader as a daemon transparently when any command is run, then wait for it to finish
|
70
|
-
* Use fsevent instead of kqueue to reduce the obscene number of file descriptors.
|
71
76
|
* Support inotify on linux
|
72
77
|
|
73
78
|
## Contributing
|
data/lib/zeus/client.rb
CHANGED
data/lib/zeus/dsl.rb
CHANGED
data/lib/zeus/process.rb
CHANGED
@@ -9,11 +9,17 @@ module Process
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
+
def self.pids_to_ppids
|
13
|
+
Hash[*`ps -eo pid,ppid`.scan(/\d+/).map(&:to_i)]
|
14
|
+
end
|
15
|
+
|
12
16
|
def self.descendants(base=Process.pid)
|
13
17
|
descendants = Hash.new{|ht,k| ht[k]=[k]}
|
14
|
-
|
18
|
+
|
19
|
+
pids_to_ppids.each do |pid,ppid|
|
15
20
|
descendants[ppid] << descendants[pid]
|
16
|
-
|
21
|
+
end
|
22
|
+
|
17
23
|
descendants[base].flatten - [base]
|
18
24
|
end
|
19
25
|
end
|
data/lib/zeus/server.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'socket'
|
3
3
|
|
4
|
-
require 'rb-kqueue'
|
5
4
|
require 'zeus/process'
|
6
5
|
|
7
6
|
module Zeus
|
@@ -27,7 +26,8 @@ module Zeus
|
|
27
26
|
@file_monitor = FileMonitor::FSEvent.new(&method(:dependency_did_change))
|
28
27
|
@acceptor_registration_monitor = AcceptorRegistrationMonitor.new
|
29
28
|
@process_tree_monitor = ProcessTreeMonitor.new
|
30
|
-
|
29
|
+
acceptor_commands = self.class.acceptors.map(&:commands).flatten
|
30
|
+
@client_handler = ClientHandler.new(acceptor_commands, acceptor_registration_monitor)
|
31
31
|
|
32
32
|
# TODO: deprecate Zeus::Server.define! maybe. We can do that better...
|
33
33
|
@plan = @@definition.to_domain_object(self)
|
@@ -56,7 +56,6 @@ module Zeus
|
|
56
56
|
def run
|
57
57
|
$0 = "zeus master"
|
58
58
|
trap("INT") { exit 0 }
|
59
|
-
at_exit { Process.killall_descendants(9) }
|
60
59
|
|
61
60
|
@r_msg, @w_msg = Socket.pair(:UNIX, :DGRAM)
|
62
61
|
|
data/lib/zeus/server/acceptor.rb
CHANGED
@@ -14,9 +14,7 @@ module Zeus
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def register_with_client_handler(pid)
|
17
|
-
@
|
18
|
-
@s_client_handler = UNIXSocket.for_fd(@a.fileno)
|
19
|
-
@s_acceptor = UNIXSocket.for_fd(@b.fileno)
|
17
|
+
@s_client_handler, @s_acceptor = UNIXSocket.pair
|
20
18
|
|
21
19
|
@s_acceptor.puts registration_data(pid)
|
22
20
|
|
@@ -27,8 +25,34 @@ module Zeus
|
|
27
25
|
{pid: pid, commands: [name, *aliases], description: description}.to_json
|
28
26
|
end
|
29
27
|
|
28
|
+
def descendent_acceptors
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def print_error(io, error)
|
33
|
+
io.puts "#{error.backtrace[0]}: #{error.message} (#{error.class})"
|
34
|
+
error.backtrace[1..-1].each do |line|
|
35
|
+
io.puts "\tfrom #{line}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def run_as_error(e)
|
40
|
+
register_with_client_handler(Process.pid)
|
41
|
+
Zeus.ui.as_zeus "starting error-state acceptor `#{@name}`"
|
42
|
+
|
43
|
+
Thread.new do
|
44
|
+
loop do
|
45
|
+
terminal = @s_acceptor.recv_io
|
46
|
+
_ = @s_acceptor.readline
|
47
|
+
@s_acceptor << 0 << "\n"
|
48
|
+
print_error(terminal, e)
|
49
|
+
terminal.close
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
30
54
|
def run
|
31
|
-
fork {
|
55
|
+
pid = fork {
|
32
56
|
$0 = "zeus acceptor: #{@name}"
|
33
57
|
pid = Process.pid
|
34
58
|
|
@@ -51,6 +75,7 @@ module Zeus
|
|
51
75
|
terminal = @s_acceptor.recv_io
|
52
76
|
arguments = JSON.parse(@s_acceptor.readline.chomp)
|
53
77
|
child = fork do
|
78
|
+
$0 = "zeus runner: #{@name}"
|
54
79
|
postfork_action!
|
55
80
|
@s_acceptor << $$ << "\n"
|
56
81
|
$stdin.reopen(terminal)
|
@@ -64,6 +89,9 @@ module Zeus
|
|
64
89
|
terminal.close
|
65
90
|
end
|
66
91
|
}
|
92
|
+
currpid = Process.pid
|
93
|
+
at_exit { Process.kill(9, pid) if Process.pid == currpid rescue nil }
|
94
|
+
pid
|
67
95
|
end
|
68
96
|
|
69
97
|
def prefork_action! # TODO : refactor
|
@@ -6,11 +6,7 @@ module Zeus
|
|
6
6
|
def on_datasource_event ; handle_registration ; end
|
7
7
|
|
8
8
|
def initialize
|
9
|
-
|
10
|
-
# and cause the UNIXSockets to quit working... often in perplexing ways.
|
11
|
-
@s, @r = Socket.pair(:UNIX, :DGRAM)
|
12
|
-
@reg_monitor = UNIXSocket.for_fd(@s.fileno)
|
13
|
-
@reg_acceptor = UNIXSocket.for_fd(@r.fileno)
|
9
|
+
@reg_monitor, @reg_acceptor = UNIXSocket.pair
|
14
10
|
@acceptors = []
|
15
11
|
end
|
16
12
|
|
@@ -27,8 +27,9 @@ module Zeus
|
|
27
27
|
def datasource ; @server ; end
|
28
28
|
def on_datasource_event ; handle_server_connection ; end
|
29
29
|
|
30
|
-
def initialize(registration_monitor)
|
30
|
+
def initialize(acceptor_commands, registration_monitor)
|
31
31
|
@reg_monitor = registration_monitor
|
32
|
+
@acceptor_commands = acceptor_commands
|
32
33
|
@server = UNIXServer.new(Zeus::SOCKET_NAME)
|
33
34
|
@server.listen(10)
|
34
35
|
rescue Errno::EADDRINUSE
|
@@ -38,7 +39,18 @@ module Zeus
|
|
38
39
|
|
39
40
|
def handle_server_connection
|
40
41
|
s_client = @server.accept
|
41
|
-
fork { handshake_client_to_acceptor(s_client) }
|
42
|
+
fork { handshake_client_to_acceptor(s_client) ; exit }
|
43
|
+
end
|
44
|
+
|
45
|
+
NoSuchCommand = Class.new(Exception)
|
46
|
+
AcceptorNotBooted = Class.new(Exception)
|
47
|
+
ApplicationLoadFailed = Class.new(Exception)
|
48
|
+
|
49
|
+
def exit_with_message(s_client, client_terminal, msg)
|
50
|
+
s_client << "0\n"
|
51
|
+
client_terminal << "[zeus] #{msg}\n"
|
52
|
+
client_terminal.close
|
53
|
+
s_client.close
|
42
54
|
end
|
43
55
|
|
44
56
|
# client clienthandler acceptor
|
@@ -57,9 +69,23 @@ module Zeus
|
|
57
69
|
client_terminal = s_client.recv_io
|
58
70
|
|
59
71
|
# 3
|
72
|
+
unless @acceptor_commands.include?(command.to_s)
|
73
|
+
return exit_with_message(
|
74
|
+
s_client, client_terminal,
|
75
|
+
"no such command `#{command}`.")
|
76
|
+
end
|
60
77
|
acceptor = @reg_monitor.find_acceptor_for_command(command)
|
78
|
+
unless acceptor
|
79
|
+
return exit_with_message(
|
80
|
+
s_client, client_terminal,
|
81
|
+
"not yet ready to process `#{command}`. Try again right away.")
|
82
|
+
end
|
61
83
|
usock = UNIXSocket.for_fd(acceptor.socket.fileno)
|
62
|
-
|
84
|
+
if usock.closed?
|
85
|
+
return exit_with_message(
|
86
|
+
s_client, client_terminal,
|
87
|
+
"`#{command}` handler is reloading a dependency. Try again right away.")
|
88
|
+
end
|
63
89
|
usock.send_io(client_terminal)
|
64
90
|
|
65
91
|
Zeus.ui.info "accepting connection for #{command}"
|
data/lib/zeus/server/stage.rb
CHANGED
@@ -9,10 +9,40 @@ module Zeus
|
|
9
9
|
@server = server
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
def notify_feature(feature)
|
13
|
+
@server.w_feature "#{Process.pid}:#{feature}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def descendent_acceptors
|
17
|
+
@stages.map(&:descendent_acceptors).flatten
|
18
|
+
end
|
19
|
+
|
20
|
+
def register_acceptors_as_errors(e)
|
21
|
+
descendent_acceptors.each do |acc|
|
22
|
+
acc.run_as_error(e)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
def handle_load_error(e)
|
28
|
+
errored_file = e.backtrace[0].scan(/(.+?):\d+:in/)[0][0]
|
29
|
+
|
30
|
+
# handle relative paths
|
31
|
+
unless errored_file =~ /^\//
|
32
|
+
errored_file = File.expand_path(errored_file, Dir.pwd)
|
33
|
+
end
|
34
|
+
|
35
|
+
register_acceptors_as_errors(e)
|
36
|
+
# register all the decendent acceptors as stubs with errors
|
37
|
+
|
38
|
+
notify_feature(errored_file)
|
39
|
+
$LOADED_FEATURES.each { |f| notify_feature(f) }
|
40
|
+
|
41
|
+
# we do not need to do anything. We wait, until a dependency changes.
|
42
|
+
# At that point, we get killed and restarted.
|
43
|
+
sleep
|
44
|
+
end
|
45
|
+
|
16
46
|
def run
|
17
47
|
@pid = fork {
|
18
48
|
$0 = "zeus spawner: #{@name}"
|
@@ -25,16 +55,18 @@ module Zeus
|
|
25
55
|
exit 0
|
26
56
|
}
|
27
57
|
|
28
|
-
|
58
|
+
begin
|
59
|
+
@actions.each(&:call)
|
60
|
+
rescue => e
|
61
|
+
handle_load_error(e)
|
62
|
+
end
|
29
63
|
|
30
64
|
pids = {}
|
31
65
|
@stages.each do |stage|
|
32
66
|
pids[stage.run] = stage
|
33
67
|
end
|
34
68
|
|
35
|
-
$LOADED_FEATURES.each
|
36
|
-
@server.w_feature "#{pid}:#{f}"
|
37
|
-
end
|
69
|
+
$LOADED_FEATURES.each { |f| notify_feature(f) }
|
38
70
|
|
39
71
|
loop do
|
40
72
|
begin
|
@@ -42,15 +74,13 @@ module Zeus
|
|
42
74
|
rescue Errno::ECHILD
|
43
75
|
raise HasNoChildren.new("Stage `#{@name}` - All terminal nodes must be acceptors")
|
44
76
|
end
|
45
|
-
|
46
|
-
|
47
|
-
else # restart the stage that died.
|
48
|
-
stage = pids[pid]
|
49
|
-
pids[stage.run] = stage
|
50
|
-
end
|
77
|
+
stage = pids[pid]
|
78
|
+
pids[stage.run] = stage
|
51
79
|
end
|
52
|
-
|
53
80
|
}
|
81
|
+
currpid = Process.pid
|
82
|
+
at_exit { Process.kill(9, @pid) if Process.pid == currpid rescue nil }
|
83
|
+
@pid
|
54
84
|
end
|
55
85
|
|
56
86
|
end
|
data/lib/zeus/ui.rb
CHANGED
data/lib/zeus/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zeus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.0
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.0
|
5
|
+
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Burke Libbey
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-08-02 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Zeus preloads pretty much everything you'll ever want to use in development.
|
15
15
|
email:
|
@@ -25,7 +25,6 @@ files:
|
|
25
25
|
- README.md
|
26
26
|
- Rakefile
|
27
27
|
- bin/zeus
|
28
|
-
- ext/.DS_Store
|
29
28
|
- ext/fsevents-wrapper/fsevents-wrapper
|
30
29
|
- ext/fsevents-wrapper/main.m
|
31
30
|
- lib/thrud.rb
|
@@ -61,9 +60,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
61
60
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
61
|
none: false
|
63
62
|
requirements:
|
64
|
-
- - ! '
|
63
|
+
- - ! '>='
|
65
64
|
- !ruby/object:Gem::Version
|
66
|
-
version:
|
65
|
+
version: '0'
|
67
66
|
requirements: []
|
68
67
|
rubyforge_project:
|
69
68
|
rubygems_version: 1.8.11
|
data/ext/.DS_Store
DELETED
Binary file
|