havanna 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/LICENSE +21 -0
- data/README.md +118 -0
- data/bin/havanna +137 -0
- data/havanna.gemspec +19 -0
- data/lib/havanna.rb +38 -0
- data/makefile +4 -0
- data/test/havanna_test.rb +228 -0
- data/test/workers/echo/Havannafile +3 -0
- data/test/workers/echo/echo.rb +7 -0
- data/test/workers/logger/Havannafile +3 -0
- data/test/workers/logger/logger.rb +8 -0
- data/test/workers/slow/Havannafile +3 -0
- data/test/workers/slow/slow.rb +8 -0
- metadata +100 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 083b1457c1f1af6aa4327822bbb00daba957324d
|
4
|
+
data.tar.gz: c5118e5d44add3623f7457caab9d8d1fda81a399
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f0e1927e6176f9fa7bb90be4524a52d136bdf8fb3c7221baadb861221fb0eb03a0030d9e5df429bc8f54baa872d23c1df4a0134fe08278577684d1effba8b78f
|
7
|
+
data.tar.gz: 55d2ebf43d9fdfcce39a20fd848c8b72fe3e9393133a8eacf56a003ddc44c87a4ca6f5847b5b495f61a1f6c4f9e57c8b372eeedff89c880439c6680c60893969
|
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Damian Janowski
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
Havanna
|
2
|
+
=======
|
3
|
+
|
4
|
+
Ruby workers with [Disque][disque].
|
5
|
+
|
6
|
+
Usage
|
7
|
+
-----
|
8
|
+
|
9
|
+
Create a worker:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
class Mailer
|
13
|
+
def call(item)
|
14
|
+
puts "Emailing #{item}..."
|
15
|
+
|
16
|
+
# Actually do it.
|
17
|
+
end
|
18
|
+
end
|
19
|
+
```
|
20
|
+
|
21
|
+
Havanna doesn't perform any kind of magic autodiscovery of
|
22
|
+
your workers. Similar to Rack's `config.ru`, Havanna has an
|
23
|
+
entry point file where you explicitly declare your workers.
|
24
|
+
This would be a valid `Havannafile`:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
require "app"
|
28
|
+
|
29
|
+
Havanna.run(Mailer)
|
30
|
+
```
|
31
|
+
|
32
|
+
Now on the command line, start your workers:
|
33
|
+
|
34
|
+
```
|
35
|
+
$ havanna start
|
36
|
+
```
|
37
|
+
|
38
|
+
In a different window, try queuing a job using Disque's
|
39
|
+
built-in client:
|
40
|
+
|
41
|
+
```
|
42
|
+
$ disque addjob Mailer foo@example.com 5000
|
43
|
+
```
|
44
|
+
|
45
|
+
Once you're up and running, deploy your workers with `-d`
|
46
|
+
for daemonization:
|
47
|
+
|
48
|
+
```
|
49
|
+
$ havanna start -d
|
50
|
+
```
|
51
|
+
|
52
|
+
Stop the worker pool by issuing a `stop` command:
|
53
|
+
|
54
|
+
```
|
55
|
+
$ havanna stop
|
56
|
+
```
|
57
|
+
|
58
|
+
This will wait for all workers to exit gracefully.
|
59
|
+
|
60
|
+
For more information, run:
|
61
|
+
|
62
|
+
```
|
63
|
+
$ havanna -h
|
64
|
+
```
|
65
|
+
|
66
|
+
|
67
|
+
Design notes
|
68
|
+
------------
|
69
|
+
|
70
|
+
Havanna assumes that your workers perform a fair amount of I/O (probably one
|
71
|
+
of the most common reasons to send jobs to the background). We will optimize
|
72
|
+
Havanna for this use case.
|
73
|
+
|
74
|
+
Currently, Havanna runs multiple threads per worker. However, we may `fork(2)`
|
75
|
+
if we find that's better for multiple-core utilization under MRI.
|
76
|
+
|
77
|
+
|
78
|
+
Alternatives
|
79
|
+
------------
|
80
|
+
|
81
|
+
It's very likely that Havanna is not for you. While I use it in production,
|
82
|
+
it's small and doesn't do much.
|
83
|
+
|
84
|
+
These are the alternatives I know of in Rubyland:
|
85
|
+
|
86
|
+
- [Disc][disc]: By my friend [pote][pote]. It supports more customization of
|
87
|
+
workers and queues, takes configuration from environment variables and can
|
88
|
+
take advantage of Celluloid if you're using it.
|
89
|
+
|
90
|
+
- [DisqueJockey][disque_jockey]: I don't know much about this one, but
|
91
|
+
apparently it's even more configurable, has a DSL and (naturally) might be
|
92
|
+
a better fit if you use/like Rails.
|
93
|
+
|
94
|
+
|
95
|
+
About the name
|
96
|
+
--------------
|
97
|
+
|
98
|
+
Havanna is inspired by [Ost][ost] and [ost(1)][ost-bin]. [soveran][soveran]
|
99
|
+
named Ost after a café, and I happened to be sitting at another café when I
|
100
|
+
started to work on this library. Its name: [Havanna][havanna].
|
101
|
+
|
102
|
+
By the way, before becoming a café, Havanna produced the best
|
103
|
+
*[alfajores][alfajores]* in Argentina. They only had one store in Mar del
|
104
|
+
Plata (~400km away from Buenos Aires), so it became a tradition to bring these
|
105
|
+
exquisite *alfajores* when you returned from a trip to the beach. Several
|
106
|
+
years later they opened stores in Buenos Aires and elsewhere and became a
|
107
|
+
coffee shop.
|
108
|
+
|
109
|
+
|
110
|
+
[alfajores]: https://www.google.com.ar/search?q=alfajor+argentino&tbm=isch
|
111
|
+
[disc]: https://github.com/pote/disc
|
112
|
+
[disque]: https://github.com/antirez/disque
|
113
|
+
[disque_jockey]: https://github.com/DevinRiley/disque_jockey
|
114
|
+
[havanna]: https://www.google.com.ar/search?q=havanna+cafe&tbm=isch
|
115
|
+
[ost-bin]: https://github.com/djanowski/ost-bin
|
116
|
+
[ost]: https://github.com/soveran/ost
|
117
|
+
[pote]: https://twitter.com/poteland
|
118
|
+
[soveran]: https://twitter.com/soveran
|
data/bin/havanna
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
stop = proc do
|
4
|
+
if defined?(Havanna)
|
5
|
+
Havanna.stop
|
6
|
+
else
|
7
|
+
exit 0
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
trap(:INT, &stop)
|
12
|
+
trap(:TERM, &stop)
|
13
|
+
|
14
|
+
usage = <<-EOS
|
15
|
+
Usage:
|
16
|
+
|
17
|
+
havanna start [-r <require>] [-d] [-p <pid-path>] [-l <log-path>]
|
18
|
+
havanna stop [-p <pid-path>]
|
19
|
+
|
20
|
+
EOS
|
21
|
+
|
22
|
+
require "clap"
|
23
|
+
require_relative "../lib/havanna"
|
24
|
+
|
25
|
+
opts = {
|
26
|
+
requires: []
|
27
|
+
}
|
28
|
+
|
29
|
+
command, _ = Clap.run ARGV,
|
30
|
+
"-d" => -> {
|
31
|
+
opts[:daemonize] = true
|
32
|
+
opts[:log_path] = File.expand_path("havanna.log") unless opts.include?(:log_path)
|
33
|
+
},
|
34
|
+
"-l" => -> path {
|
35
|
+
opts[:log_path] = path
|
36
|
+
},
|
37
|
+
"-p" => -> path {
|
38
|
+
opts[:pid_path] = path
|
39
|
+
},
|
40
|
+
"-s" => -> size {
|
41
|
+
opts[:pool_size] = Integer(size)
|
42
|
+
},
|
43
|
+
"-r" => -> file {
|
44
|
+
opts[:requires] << file
|
45
|
+
},
|
46
|
+
"-v" => -> {
|
47
|
+
require_relative "../lib/havanna/version"
|
48
|
+
|
49
|
+
puts Havanna::VERSION
|
50
|
+
|
51
|
+
exit 0
|
52
|
+
},
|
53
|
+
"-h" => -> {
|
54
|
+
puts(usage)
|
55
|
+
exit 0
|
56
|
+
}
|
57
|
+
|
58
|
+
opts[:pid_path] = File.expand_path("havanna.pid") unless opts.include?(:pid_path)
|
59
|
+
|
60
|
+
opts[:requires].each do |file|
|
61
|
+
require(file)
|
62
|
+
end
|
63
|
+
|
64
|
+
module Havanna
|
65
|
+
def self.run(worker)
|
66
|
+
workers << worker
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.workers
|
70
|
+
@workers ||= []
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
case command
|
75
|
+
when "start"
|
76
|
+
if opts[:daemonize]
|
77
|
+
Process.daemon(true)
|
78
|
+
|
79
|
+
File.open(opts[:pid_path], File::RDWR|File::EXCL|File::CREAT, 0600) do |io|
|
80
|
+
io.write(Process.pid)
|
81
|
+
end
|
82
|
+
|
83
|
+
at_exit do
|
84
|
+
File.delete(opts[:pid_path]) if File.exists?(opts[:pid_path])
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
if opts[:log_path]
|
89
|
+
$stdout.reopen(opts[:log_path], "a")
|
90
|
+
$stderr.reopen(opts[:log_path], "a")
|
91
|
+
end
|
92
|
+
|
93
|
+
load "./Havannafile"
|
94
|
+
|
95
|
+
opts[:pool_size] = Havanna.workers.size unless opts.include?(:pool_size)
|
96
|
+
|
97
|
+
threads_per_worker = opts[:pool_size] / Havanna.workers.size
|
98
|
+
|
99
|
+
if threads_per_worker == 0
|
100
|
+
abort("Not enough threads for your workers (found #{Havanna.workers.size} workers).")
|
101
|
+
end
|
102
|
+
|
103
|
+
pool = Havanna.workers.each_with_object([]) do |worker, accum|
|
104
|
+
accum.concat(Array.new(threads_per_worker) do
|
105
|
+
Thread.new(worker) do |worker|
|
106
|
+
Thread.current.abort_on_exception = true
|
107
|
+
Havanna.start(worker)
|
108
|
+
end
|
109
|
+
end)
|
110
|
+
end
|
111
|
+
|
112
|
+
pool.each(&:join)
|
113
|
+
|
114
|
+
when "stop"
|
115
|
+
if File.exist?(opts[:pid_path])
|
116
|
+
pid = Integer(File.read(opts[:pid_path]).chomp)
|
117
|
+
|
118
|
+
running = true
|
119
|
+
|
120
|
+
Process.kill(:TERM, pid)
|
121
|
+
|
122
|
+
while running
|
123
|
+
begin
|
124
|
+
Process.kill(0, pid)
|
125
|
+
running = true
|
126
|
+
rescue Errno::ESRCH
|
127
|
+
running = false
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
else
|
133
|
+
$stderr.puts("Unkown command #{command.inspect}.")
|
134
|
+
$stderr.puts(usage)
|
135
|
+
|
136
|
+
exit 2
|
137
|
+
end
|
data/havanna.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative "lib/havanna"
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "havanna"
|
5
|
+
s.version = Havanna::VERSION
|
6
|
+
s.summary = "Ruby workers for Disque."
|
7
|
+
s.authors = ["Damian Janowski"]
|
8
|
+
s.email = ["damian.janowski@gmail.com"]
|
9
|
+
s.homepage = "https://github.com/djanowski/havanna"
|
10
|
+
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
|
13
|
+
s.executables << "havanna"
|
14
|
+
|
15
|
+
s.add_dependency "disque"
|
16
|
+
s.add_dependency "clap"
|
17
|
+
|
18
|
+
s.add_development_dependency "cutest"
|
19
|
+
end
|
data/lib/havanna.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require "disque"
|
2
|
+
|
3
|
+
module Havanna
|
4
|
+
VERSION = "1.0.0"
|
5
|
+
|
6
|
+
def self.connect(*args)
|
7
|
+
@connect = args
|
8
|
+
@disque = Disque.new(*args)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.start(worker)
|
12
|
+
instance = worker.new
|
13
|
+
|
14
|
+
begin
|
15
|
+
disque = Disque.new(*@connect)
|
16
|
+
|
17
|
+
printf("Started worker %s\n", worker)
|
18
|
+
|
19
|
+
loop do
|
20
|
+
disque.fetch(from: [worker.name], timeout: 5000) do |job|
|
21
|
+
instance.call(job)
|
22
|
+
end
|
23
|
+
|
24
|
+
break if @stop
|
25
|
+
end
|
26
|
+
ensure
|
27
|
+
disque.quit
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.push(*args)
|
32
|
+
@disque.push(*args)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.stop
|
36
|
+
@stop = true
|
37
|
+
end
|
38
|
+
end
|
data/makefile
ADDED
@@ -0,0 +1,228 @@
|
|
1
|
+
require "cutest"
|
2
|
+
require "timeout"
|
3
|
+
require "disque"
|
4
|
+
|
5
|
+
at_exit {
|
6
|
+
Process.waitall
|
7
|
+
}
|
8
|
+
|
9
|
+
def wait_for_pid(pid)
|
10
|
+
wait_for { !running?(pid) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def wait_for_child(pid)
|
14
|
+
Timeout.timeout(5) do
|
15
|
+
Process.wait(pid)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def wait_for
|
20
|
+
Timeout.timeout(10) do
|
21
|
+
until value = yield
|
22
|
+
sleep(0.1)
|
23
|
+
end
|
24
|
+
|
25
|
+
return value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def running?(pid)
|
30
|
+
begin
|
31
|
+
Process.kill(0, pid)
|
32
|
+
true
|
33
|
+
rescue Errno::ESRCH
|
34
|
+
false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def read_pid_file(path)
|
39
|
+
wait_for { File.exist?(path) && File.size(path) > 0 }
|
40
|
+
|
41
|
+
Integer(File.read(path))
|
42
|
+
end
|
43
|
+
|
44
|
+
def root(path)
|
45
|
+
File.expand_path("../#{path}", File.dirname(__FILE__))
|
46
|
+
end
|
47
|
+
|
48
|
+
disque = Disque.new("127.0.0.1:7711")
|
49
|
+
|
50
|
+
prepare do
|
51
|
+
disque.call("DEBUG", "FLUSHALL")
|
52
|
+
Dir["test/workers/**/*.pid"].each { |file| File.delete(file) }
|
53
|
+
end
|
54
|
+
|
55
|
+
test "start" do
|
56
|
+
pid = nil
|
57
|
+
|
58
|
+
begin
|
59
|
+
pid = spawn("#{root("bin/havanna")} start", chdir: "test/workers/echo")
|
60
|
+
|
61
|
+
disque.push("Echo", 2, 5000)
|
62
|
+
|
63
|
+
job = wait_for { disque.fetch(from: ["Echo:result"]) }
|
64
|
+
|
65
|
+
assert_equal "2", job[0][-1]
|
66
|
+
ensure
|
67
|
+
Process.kill(:INT, pid) if pid
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
test "gracefully handles TERM signals" do
|
72
|
+
disque.push("Slow", 3, 5000)
|
73
|
+
|
74
|
+
begin
|
75
|
+
spawn("#{root("bin/havanna")} -d start", chdir: "test/workers/slow")
|
76
|
+
|
77
|
+
pid = read_pid_file("./test/workers/slow/havanna.pid")
|
78
|
+
|
79
|
+
assert wait_for { disque.call("QLEN", "Slow:result") == 0 }
|
80
|
+
ensure
|
81
|
+
Process.kill(:TERM, pid) if pid
|
82
|
+
end
|
83
|
+
|
84
|
+
wait_for_pid(pid)
|
85
|
+
|
86
|
+
assert_equal "3", disque.fetch(from: ["Slow:result"])[0][-1]
|
87
|
+
end
|
88
|
+
|
89
|
+
test "stop waits for workers to be done" do
|
90
|
+
spawn("#{root("bin/havanna")} start -d", chdir: "test/workers/slow")
|
91
|
+
|
92
|
+
pid = read_pid_file("./test/workers/slow/havanna.pid")
|
93
|
+
|
94
|
+
stopper = spawn("#{root("bin/havanna")} stop", chdir: "test/workers/slow")
|
95
|
+
|
96
|
+
# Let the stop command start.
|
97
|
+
wait_for { running?(stopper) }
|
98
|
+
|
99
|
+
# Let the stop command end.
|
100
|
+
wait_for_child(stopper)
|
101
|
+
|
102
|
+
# Immediately after the stop command exits,
|
103
|
+
# havanna(1) shouldn't be running and the pid file
|
104
|
+
# should be gone.
|
105
|
+
|
106
|
+
assert !running?(pid)
|
107
|
+
assert !File.exist?("./test/workers/slow/havanna.pid")
|
108
|
+
end
|
109
|
+
|
110
|
+
test "use a specific path for the pid file" do
|
111
|
+
pid = nil
|
112
|
+
pid_path = "./test/workers/echo/foo.pid"
|
113
|
+
|
114
|
+
begin
|
115
|
+
spawn("#{root("bin/havanna")} -d start -p foo.pid", chdir: "test/workers/echo")
|
116
|
+
|
117
|
+
pid = read_pid_file(pid_path)
|
118
|
+
|
119
|
+
assert pid
|
120
|
+
ensure
|
121
|
+
Process.kill(:INT, pid) if pid
|
122
|
+
end
|
123
|
+
|
124
|
+
wait_for_pid(pid)
|
125
|
+
|
126
|
+
assert !File.exist?(pid_path)
|
127
|
+
end
|
128
|
+
|
129
|
+
test "load Havannafile" do
|
130
|
+
pid = nil
|
131
|
+
|
132
|
+
begin
|
133
|
+
pid = spawn("#{root("bin/havanna")} start", chdir: "test/workers/echo")
|
134
|
+
|
135
|
+
disque.push("Echo", 2, 5000)
|
136
|
+
|
137
|
+
value = wait_for { disque.fetch(from: ["Echo:result"]) }
|
138
|
+
|
139
|
+
assert_equal "2", value
|
140
|
+
ensure
|
141
|
+
Process.kill(:INT, pid) if pid
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
test "redirect stdout and stderr to a log file when daemonizing" do
|
146
|
+
pid, detached_pid = nil
|
147
|
+
|
148
|
+
pid_path = "./test/workers/logger/havanna.pid"
|
149
|
+
|
150
|
+
log_path = "test/workers/logger/havanna.log"
|
151
|
+
|
152
|
+
File.delete(log_path) if File.exist?(log_path)
|
153
|
+
|
154
|
+
begin
|
155
|
+
pid = spawn("#{root("bin/havanna")} -d start", chdir: "test/workers/logger")
|
156
|
+
|
157
|
+
assert wait_for {
|
158
|
+
`ps -p #{pid} -o state`.lines.to_a.last[/(\w+)/, 1] == "Z"
|
159
|
+
}
|
160
|
+
|
161
|
+
redis.lpush("Logger", 1)
|
162
|
+
ensure
|
163
|
+
detached_pid = read_pid_file(pid_path)
|
164
|
+
|
165
|
+
Process.kill(:INT, pid) if pid
|
166
|
+
Process.kill(:INT, detached_pid) if detached_pid
|
167
|
+
end
|
168
|
+
|
169
|
+
wait_for_pid(detached_pid)
|
170
|
+
|
171
|
+
assert_equal "out: 1\nerr: 1\n", File.read(log_path)
|
172
|
+
end
|
173
|
+
|
174
|
+
test "redirect stdout and stderr to a different log file when daemonizing" do
|
175
|
+
pid, detached_pid = nil
|
176
|
+
|
177
|
+
pid_path = "./test/workers/logger/havanna.pid"
|
178
|
+
|
179
|
+
log_path = "test/workers/logger/foo.log"
|
180
|
+
|
181
|
+
File.delete(log_path) if File.exist?(log_path)
|
182
|
+
|
183
|
+
begin
|
184
|
+
pid = spawn("#{root("bin/havanna")} -d -l foo.log start", chdir: "test/workers/logger")
|
185
|
+
|
186
|
+
assert wait_for {
|
187
|
+
`ps -p #{pid} -o state`.lines.to_a.last[/(\w+)/, 1] == "Z"
|
188
|
+
}
|
189
|
+
|
190
|
+
redis.lpush("Logger", 1)
|
191
|
+
ensure
|
192
|
+
detached_pid = read_pid_file(pid_path)
|
193
|
+
|
194
|
+
Process.kill(:INT, pid) if pid
|
195
|
+
Process.kill(:INT, detached_pid) if detached_pid
|
196
|
+
end
|
197
|
+
|
198
|
+
wait_for_pid(detached_pid)
|
199
|
+
|
200
|
+
assert_equal "out: 1\nerr: 1\n", File.read(log_path)
|
201
|
+
end
|
202
|
+
|
203
|
+
test "daemonizes" do
|
204
|
+
pid, detached_pid = nil
|
205
|
+
|
206
|
+
pid_path = "./test/workers/echo/havanna.pid"
|
207
|
+
|
208
|
+
begin
|
209
|
+
pid = spawn("#{root("bin/havanna")} -d start", chdir: "test/workers/echo")
|
210
|
+
|
211
|
+
assert wait_for {
|
212
|
+
`ps -p #{pid} -o state`.lines.to_a.last[/(\w+)/, 1] == "Z"
|
213
|
+
}
|
214
|
+
|
215
|
+
detached_pid = read_pid_file(pid_path)
|
216
|
+
|
217
|
+
ppid = `ps -p #{detached_pid} -o ppid`.lines.to_a.last[/(\d+)/, 1]
|
218
|
+
|
219
|
+
assert_equal "1", ppid
|
220
|
+
ensure
|
221
|
+
Process.kill(:INT, pid) if pid
|
222
|
+
Process.kill(:INT, detached_pid) if detached_pid
|
223
|
+
end
|
224
|
+
|
225
|
+
wait_for_pid(detached_pid)
|
226
|
+
|
227
|
+
assert !File.exist?(pid_path)
|
228
|
+
end
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: havanna
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Damian Janowski
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: disque
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: clap
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: cutest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description:
|
56
|
+
email:
|
57
|
+
- damian.janowski@gmail.com
|
58
|
+
executables:
|
59
|
+
- havanna
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- .gitignore
|
64
|
+
- LICENSE
|
65
|
+
- README.md
|
66
|
+
- bin/havanna
|
67
|
+
- havanna.gemspec
|
68
|
+
- lib/havanna.rb
|
69
|
+
- makefile
|
70
|
+
- test/havanna_test.rb
|
71
|
+
- test/workers/echo/Havannafile
|
72
|
+
- test/workers/echo/echo.rb
|
73
|
+
- test/workers/logger/Havannafile
|
74
|
+
- test/workers/logger/logger.rb
|
75
|
+
- test/workers/slow/Havannafile
|
76
|
+
- test/workers/slow/slow.rb
|
77
|
+
homepage: https://github.com/djanowski/havanna
|
78
|
+
licenses: []
|
79
|
+
metadata: {}
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 2.4.6
|
97
|
+
signing_key:
|
98
|
+
specification_version: 4
|
99
|
+
summary: Ruby workers for Disque.
|
100
|
+
test_files: []
|