havanna 1.0.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.
- 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: []
|