daemon_controller 1.2.0 → 3.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 +5 -13
- data/LICENSE.txt +1 -2
- data/{README.markdown → README.md} +108 -142
- data/Rakefile +16 -574
- data/daemon_controller.gemspec +5 -4
- data/lib/daemon_controller/lock_file.rb +106 -105
- data/lib/daemon_controller/packaging.rb +10 -16
- data/lib/daemon_controller/spawn.rb +8 -7
- data/lib/daemon_controller/version.rb +10 -8
- data/lib/daemon_controller.rb +837 -851
- data/spec/daemon_controller_spec.rb +686 -409
- data/spec/echo_server.rb +147 -115
- data/spec/test_helper.rb +178 -105
- data/spec/unresponsive_daemon.rb +9 -9
- metadata +7 -21
- checksums.yaml.gz.asc +0 -12
- data/debian.template/changelog +0 -5
- data/debian.template/compat +0 -1
- data/debian.template/control.template +0 -15
- data/debian.template/copyright +0 -24
- data/debian.template/ruby-daemon-controller.install +0 -2
- data/debian.template/rules +0 -17
- data/debian.template/source/format +0 -1
- data/rpm/get_distro_id.py +0 -4
- data/rpm/rubygem-daemon_controller.spec.template +0 -102
- data/spec/run_echo_server +0 -9
- data.tar.gz.asc +0 -12
- metadata.gz.asc +0 -12
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
MTUyNDIxMmExMmNlMTFjOWQ1ZjVhODdmM2ExOTUwMTQ2YTk0MjM2MQ==
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: bd4f2d08b82c7340e6b00c8a97e63f66a6677fbc28e9b705ecb6c53d75701e00
|
4
|
+
data.tar.gz: 11bc6d68d7837db49c123b5e2b08fc932bf60c1b0c29742e7e891e66d8f4f82a
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
ODBmMGQxMWIwYTc3ZmRlYTJlZmQwNGNiOGQ1MmZiNDE2MzI1NTFjZGY3NjE3
|
11
|
-
MWQ1ZjExYzc1ZTI2MDM3YTc2ZjhhZjJmMGFkZDZkMTY2NTQzYmI=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
ZWI1NmQ1Y2U5NjRlZGUxZDE0ODA3NTMwMDQ2NWM5NjlkMjYxZTgxYTFkYTA2
|
14
|
-
ZWY2NjUxYjczZTQ5YWJjODM1MWZlODQxY2UyOTc2M2I5NjVmOGUwNTZmMWNl
|
15
|
-
MGZkZTUwODg0NDFjNmU2MzZlODI2ZTZhZmZiMmYwYjEwMWQ1ZDg=
|
6
|
+
metadata.gz: c29f7ea6b92d607774bef4f42dba2e03215f85078079fb3febd404cf87efff8f0536e03ba870234c72925f7404142dba2bf73217d7586175328d6611ba72e3d8
|
7
|
+
data.tar.gz: 1b5d67e7165fb4c2ba1c667c774e089a5a5029e57ba47dd1f91cbcd17e2dcdef7136947b0d115d71b7db9c154013043f9da01428d55659898eee198bd1ca3d3a
|
data/LICENSE.txt
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2008-
|
1
|
+
Copyright (c) 2008-2025 Asynchronous B.V.
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
4
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -17,4 +17,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
17
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
18
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
THE SOFTWARE.
|
20
|
-
|
@@ -12,11 +12,11 @@ It provides the following functionality:
|
|
12
12
|
* Starting daemons. If the daemon fails to start then an exception will be
|
13
13
|
raised. *daemon_controller* can even detect failures that occur after the
|
14
14
|
daemon has already daemonized.
|
15
|
-
|
15
|
+
|
16
16
|
Starting daemons is done in a race-condition-free manner. If another
|
17
17
|
process using *daemon_controller* is trying to start the same daemon,
|
18
18
|
then *daemon_controller* will guarantee serialization.
|
19
|
-
|
19
|
+
|
20
20
|
*daemon_controller* also raises an exception if it detects that the daemon
|
21
21
|
is already started.
|
22
22
|
* Connecting to a daemon, starting it if it's not already started. This too
|
@@ -25,53 +25,11 @@ It provides the following functionality:
|
|
25
25
|
* Stopping daemons.
|
26
26
|
* Checking whether a daemon is running.
|
27
27
|
|
28
|
-
## Installation
|
29
|
-
|
30
|
-
gem install daemon_controller
|
31
|
-
|
32
|
-
This gem is signed using PGP with the [Phusion Software Signing key](http://www.phusion.nl/about/gpg). That key in turn is signed by [the rubygems-openpgp Certificate Authority](http://www.rubygems-openpgp-ca.org/).
|
33
|
-
|
34
|
-
You can verify the authenticity of the gem by following [The Complete Guide to Verifying Gems with rubygems-openpgp](http://www.rubygems-openpgp-ca.org/blog/the-complete-guide-to-verifying-gems-with-rubygems-openpgp.html).
|
35
|
-
|
36
|
-
## Installation on Ubuntu
|
37
|
-
|
38
|
-
Use our [PPA](https://launchpad.net/~phusion.nl/+archive/misc):
|
39
|
-
|
40
|
-
sudo add-apt-repository ppa:phusion.nl/misc
|
41
|
-
sudo apt-get update
|
42
|
-
sudo apt-get install ruby-daemon-controller
|
43
|
-
|
44
|
-
## Installation on Debian
|
45
|
-
|
46
|
-
Our Ubuntu Lucid packages are compatible with Debian 6.
|
28
|
+
## Installation
|
47
29
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
sudo apt-get update
|
52
|
-
sudo apt-get install ruby-daemon-controller
|
53
|
-
|
54
|
-
## Installation on RHEL, CentOS and Amazon Linux
|
55
|
-
|
56
|
-
Enable our YUM repository:
|
57
|
-
|
58
|
-
# RHEL 6, CentOS 6
|
59
|
-
curl -L https://oss-binaries.phusionpassenger.com/yumgems/phusion-misc/el.repo | \
|
60
|
-
sudo tee /etc/yum.repos.d/phusion-misc.repo
|
61
|
-
|
62
|
-
# Amazon Linux
|
63
|
-
curl -L https://oss-binaries.phusionpassenger.com/yumgems/phusion-misc/amazon.repo | \
|
64
|
-
sudo tee /etc/yum.repos.d/phusion-misc.repo
|
65
|
-
|
66
|
-
Install our package:
|
67
|
-
|
68
|
-
sudo yum install rubygem-daemon_controller
|
69
|
-
|
70
|
-
## Resources
|
71
|
-
|
72
|
-
* [Website](https://github.com/FooBarWidget/daemon_controller)
|
73
|
-
* [RDoc](http://rdoc.info/projects/FooBarWidget/daemon_controller)
|
74
|
-
* [Git repository](git://github.com/FooBarWidget/daemon_controller.git)
|
30
|
+
```bash
|
31
|
+
gem install daemon_controller
|
32
|
+
```
|
75
33
|
|
76
34
|
|
77
35
|
What is it for?
|
@@ -123,13 +81,15 @@ daemon varies. We've observed that waiting a fixed amount of time is by far the
|
|
123
81
|
most common way. For example, UltraSphinx's daemon starting code looks like
|
124
82
|
this:
|
125
83
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
84
|
+
```ruby
|
85
|
+
system "searchd --config '#{Ultrasphinx::CONF_PATH}'"
|
86
|
+
sleep(4) # give daemon a chance to write the pid file
|
87
|
+
if ultrasphinx_daemon_running?
|
88
|
+
say "started successfully"
|
89
|
+
else
|
90
|
+
say "failed to start"
|
91
|
+
end
|
92
|
+
```
|
133
93
|
|
134
94
|
This is in no way a slam against UltraSphinx. However, if the daemon starts in
|
135
95
|
200 miliseconds, then the user who issued the start command will be waiting for
|
@@ -168,11 +128,11 @@ From the problem descriptions, it would become apparent that our wishlist is as
|
|
168
128
|
follows. Why is this wishlist often not implemented? Let's go over them.
|
169
129
|
|
170
130
|
- **A daemon should be automatically started on demand, instead of requiring the user to manually start it.**
|
171
|
-
|
131
|
+
|
172
132
|
The most obvious problems are related to concurrency. Suppose that your web
|
173
133
|
application has a search box, and you want to start the search daemon if it
|
174
134
|
isn't already started, then connect to. Two problems will arise:
|
175
|
-
|
135
|
+
|
176
136
|
* Suppose that Rails process A is still starting the daemon. At the same
|
177
137
|
time, another visitor tries to search something, and Rails process B
|
178
138
|
notices that the daemon is not running. If B tries to start the daemon
|
@@ -183,24 +143,24 @@ follows. Why is this wishlist often not implemented? Let's go over them.
|
|
183
143
|
start. For example, if you wait 2 seconds, then try to connect to the
|
184
144
|
daemon, and the daemon isn't done initializing yet, then it will seem as
|
185
145
|
if the daemon failed to start.
|
186
|
-
|
146
|
+
|
187
147
|
These are the most probable reasons why people don't try to write
|
188
148
|
auto-starting code, and instead require the user to start the daemon
|
189
149
|
manually.
|
190
|
-
|
150
|
+
|
191
151
|
These problems, as well as several less obvious problems, are closely
|
192
152
|
related to the next few points.
|
193
|
-
|
153
|
+
|
194
154
|
- **The daemon starter must wait until the daemon is done initializing, no longer and no shorter**
|
195
|
-
|
155
|
+
|
196
156
|
Because only after the daemon is fully initialized, is it safe to connect
|
197
157
|
to it. And because the user should not have to wait longer than he really
|
198
158
|
has to. During startup, the daemon will have to be continuously checked
|
199
159
|
whether it's done initializing or whether an error occured. Writing this
|
200
160
|
code can be quite a hassle, which is why most people don't do it.
|
201
|
-
|
161
|
+
|
202
162
|
- **The daemon starter must report any startup errors**
|
203
|
-
|
163
|
+
|
204
164
|
If the daemon starting command - e.g. `sphinx -c config_file.conf`,
|
205
165
|
`apachectl start` or `mongrel_rails cluster::start` - reports startup
|
206
166
|
errors, then all is fine as long as the user is starting the command from a
|
@@ -208,16 +168,16 @@ follows. Why is this wishlist often not implemented? Let's go over them.
|
|
208
168
|
already gone into the background. Such errors are only reported to the log
|
209
169
|
file.
|
210
170
|
*The daemon starter should also check the log file for any startup errors.*
|
211
|
-
|
171
|
+
|
212
172
|
Furthermore, it should be able to raise startup errors as exceptions. This
|
213
173
|
allows the the application to decide what to do with the error. For less
|
214
174
|
experienced system administrators, the error might be displayed in the
|
215
175
|
browser, allowing the administrators to become aware of the problem without
|
216
176
|
forcing them to manually check the log files. Or the error might be emailed
|
217
177
|
to a system administrator's email address.
|
218
|
-
|
178
|
+
|
219
179
|
- **The daemon starter must be able to correct stale or corrupted PID files**
|
220
|
-
|
180
|
+
|
221
181
|
If the PID file is stale, or for some reason has been corrupted, then the
|
222
182
|
daemon starter must be able to cope with that.
|
223
183
|
*It should check whether the PID file contains a valid PID, and whether the PID exists.*
|
@@ -292,28 +252,28 @@ or [God](http://god.rubyforge.org/). Rather, it is a solution to the following
|
|
292
252
|
problem:
|
293
253
|
|
294
254
|
> **Hongli:** hey Ninh, do a 'git pull', I just implemented awesome searching
|
295
|
-
> features in our application!
|
296
|
-
> **Ninh:** cool. *pulls from repository*
|
297
|
-
> **Ninh:** hey Hongli, it doesn't work.
|
298
|
-
> **Hongli:** what do you mean, it doesn't work?
|
299
|
-
> **Ninh:** it says "connection refused", or something
|
255
|
+
> features in our application!
|
256
|
+
> **Ninh:** cool. *pulls from repository*
|
257
|
+
> **Ninh:** hey Hongli, it doesn't work.
|
258
|
+
> **Hongli:** what do you mean, it doesn't work?
|
259
|
+
> **Ninh:** it says "connection refused", or something
|
300
260
|
> **Hongli:** oh I forgot to mention it, you have to run the Sphinx search
|
301
261
|
> daemon before it works. type "rake sphinx:daemon:start" to do
|
302
|
-
> that
|
262
|
+
> that
|
303
263
|
> **Ninh:** great. but now I get a different error. something about
|
304
|
-
> BackgrounDRb.
|
264
|
+
> BackgrounDRb.
|
305
265
|
> **Hongli:** oops, I forgot to mention this too. you need to start the
|
306
|
-
> BackgrounDRb server with "rake backgroundrb:start_server"
|
266
|
+
> BackgrounDRb server with "rake backgroundrb:start_server"
|
307
267
|
> **Ninh:** okay, so every time I want to use this app, I have to type
|
308
268
|
> "rake sphinx:daemon:start", "rake backgroundrb:start_server" and
|
309
|
-
> "./script/server"?
|
269
|
+
> "./script/server"?
|
310
270
|
> **Hongli:** yep
|
311
271
|
|
312
272
|
Imagine the above conversation becoming just:
|
313
273
|
|
314
274
|
> **Hongli:** hey Ninh, do a 'git pull', I just implemented awesome searching
|
315
|
-
> features in our application!
|
316
|
-
> **Ninh:** cool. *pulls from repository*
|
275
|
+
> features in our application!
|
276
|
+
> **Ninh:** cool. *pulls from repository*
|
317
277
|
> **Ninh:** awesome, it works!
|
318
278
|
|
319
279
|
This is not something that can be achieved with Monit/God. Monit/God are for
|
@@ -327,7 +287,7 @@ without having to start the daemons manually.
|
|
327
287
|
Tutorial #1: controlling Apache
|
328
288
|
===============================
|
329
289
|
|
330
|
-
Suppose that you're a [Phusion Passenger](
|
290
|
+
Suppose that you're a [Phusion Passenger](https://www.phusionpasseenger.com/) developer,
|
331
291
|
and you need to write tests for the Apache module. In particular, you want to
|
332
292
|
test whether the different Phusion Passenger configuration directives are
|
333
293
|
working as expected. Obviously, to test the Apache module, the Apache web
|
@@ -342,29 +302,30 @@ server must be running. For every test, you will want the unit test suite to:
|
|
342
302
|
|
343
303
|
That can be done with the following code:
|
344
304
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
305
|
+
```ruby
|
306
|
+
require "daemon_controller"
|
307
|
+
|
308
|
+
File.open("apache.conf", "w") do |f|
|
309
|
+
f.write("PidFile apache.pid\n")
|
310
|
+
f.write("LogFile apache.log\n")
|
311
|
+
f.write("Listen 1234\n")
|
312
|
+
f.write(... other relevant configuration options ...)
|
313
|
+
end
|
314
|
+
|
315
|
+
controller = DaemonController.new(
|
316
|
+
identifier: "Apache web server",
|
317
|
+
start_command: "apachectl -f apache.conf -k start",
|
318
|
+
ping_command: [:tcp, "localhost", 1234],
|
319
|
+
pid_file: "apache.pid",
|
320
|
+
log_file: "apache.log"
|
321
|
+
)
|
322
|
+
controller.start
|
323
|
+
|
324
|
+
# .... apache is now started ....
|
325
|
+
# .... some test code here ....
|
326
|
+
|
327
|
+
controller.stop
|
328
|
+
```
|
368
329
|
|
369
330
|
The `File.open` line is obvious: it writes the relevant Apache configuration
|
370
331
|
file.
|
@@ -372,8 +333,8 @@ file.
|
|
372
333
|
The next line is for creating a new DaemonController object. We pass a
|
373
334
|
human-readable identifier for this daemon ("Apache web server") to the
|
374
335
|
constructor. This is used for generating friendlier error messages.
|
375
|
-
We also tell it how Apache is supposed to be started (
|
376
|
-
check whether it can be connected to (
|
336
|
+
We also tell it how Apache is supposed to be started (`start_command:`), how to
|
337
|
+
check whether it can be connected to (`ping_command:`), and where its PID file
|
377
338
|
and log file is. If Apache failed with an error during startup, then it will be
|
378
339
|
reported. If Apache failed with an error after it has gone into the background,
|
379
340
|
then that will be reported too: the given log file is monitored for new error
|
@@ -381,7 +342,8 @@ messages.
|
|
381
342
|
Finally, a timeout of 25 seconds is given. If Apache doesn't start within 25
|
382
343
|
seconds, then an exception will be raised.
|
383
344
|
|
384
|
-
The ping command
|
345
|
+
The ping command specifies which socket to connect to in order to check whether
|
346
|
+
the daemon is ready. It can also be a `Proc` which returns true or false. If the Proc
|
385
347
|
raises `Errno::ECONNREFUSED`, then that's also interpreted by DaemonController
|
386
348
|
as meaning that the daemon isn't responding yet.
|
387
349
|
|
@@ -428,41 +390,43 @@ isn't running.
|
|
428
390
|
|
429
391
|
This can be achieved with the following code:
|
430
392
|
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
end
|
461
|
-
|
462
|
-
...
|
393
|
+
```ruby
|
394
|
+
require "daemon_controller"
|
395
|
+
|
396
|
+
class SearchServer
|
397
|
+
SEARCH_SERVER_PORT = 1234
|
398
|
+
|
399
|
+
def initialize
|
400
|
+
@controller = DaemonController.new(
|
401
|
+
identifier: "Sphinx search server",
|
402
|
+
start_command: "searchd -c config/sphinx.conf",
|
403
|
+
before_start: method(:before_start),
|
404
|
+
ping_command: [:tcp, "localhost", SEARCH_SERVER_PORT],
|
405
|
+
pid_file: "tmp/pids/sphinx.pid",
|
406
|
+
log_file: "log/sphinx.log")
|
407
|
+
end
|
408
|
+
|
409
|
+
def query(search_terms)
|
410
|
+
socket = @controller.connect do
|
411
|
+
TCPSocket.new("localhost", SEARCH_SERVER_PORT)
|
412
|
+
end
|
413
|
+
send_query(socket, search_terms)
|
414
|
+
retrieve_results(socket)
|
415
|
+
end
|
416
|
+
|
417
|
+
private
|
418
|
+
def before_start
|
419
|
+
generate_configuration_file
|
420
|
+
if !index_exists?
|
421
|
+
generate_index
|
463
422
|
end
|
423
|
+
end
|
464
424
|
|
465
|
-
|
425
|
+
# ...
|
426
|
+
end
|
427
|
+
```
|
428
|
+
|
429
|
+
Notice the `before_start:` option. We pass a block of code which is to be run,
|
466
430
|
just before the daemon is started. This block, along with starting the daemon,
|
467
431
|
is completely serialized. That is, if you're inside the block, then it's
|
468
432
|
guaranteed that no other process is running this block at the same time as well.
|
@@ -506,11 +470,11 @@ synchronization. This has a few implications:
|
|
506
470
|
Multiple threads can safely use daemon_controller concurrently. Multiple
|
507
471
|
processes can safely use daemon_controller concurrently. There will be no
|
508
472
|
race conditions.
|
509
|
-
|
473
|
+
|
510
474
|
However `flock()` is not implemented on Solaris. daemon_controller, if
|
511
475
|
used in MRI does not currently work on Solaris. You need to use JRuby
|
512
476
|
which does not use `flock()` to implement `File#flock`.
|
513
|
-
|
477
|
+
|
514
478
|
* On JRuby `File#flock` is implemented through the Java file locking API,
|
515
479
|
which on Unix is implemented with the `fcntl()` system calls. This is a
|
516
480
|
different kind of lock with very strange semantics.
|
@@ -524,7 +488,7 @@ synchronization. This has a few implications:
|
|
524
488
|
cannot be used to synchronize threads. If a thread has obtained a file
|
525
489
|
lock, then another thread within the same JVM process will not block upon
|
526
490
|
trying to lock the same file.
|
527
|
-
|
491
|
+
|
528
492
|
In other words, if you're on JRuby then don't concurrently access
|
529
493
|
daemon_controller from multiple threads without manual locking. Also be
|
530
494
|
careful with mixing MRI processes that use daemon_controller with JRuby
|
@@ -534,5 +498,7 @@ synchronization. This has a few implications:
|
|
534
498
|
API documentation
|
535
499
|
=================
|
536
500
|
|
537
|
-
Detailed API documentation is available
|
538
|
-
|
501
|
+
Detailed API documentation is available here:
|
502
|
+
- [Configuration options](doc/OPTIONS.md)
|
503
|
+
- [Stop flow](doc/STOP_FLOW.md)
|
504
|
+
- Inline comments in `lib/daemon_controller.rb`.
|