zeus 0.2.0.beta2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|