qless-pool 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog.md +3 -0
- data/LICENSE.txt +20 -0
- data/README.md +159 -0
- data/Rakefile +30 -0
- data/bin/qless-pool +7 -0
- data/features/basic_daemon_config.feature +68 -0
- data/features/step_definitions/daemon_steps.rb +33 -0
- data/features/step_definitions/qless-pool_steps.rb +156 -0
- data/features/support/aruba_daemon_support.rb +76 -0
- data/features/support/env.rb +1 -0
- data/lib/qless/pool.rb +415 -0
- data/lib/qless/pool/cli.rb +136 -0
- data/lib/qless/pool/logging.rb +65 -0
- data/lib/qless/pool/pool_factory.rb +43 -0
- data/lib/qless/pool/pooled_worker.rb +21 -0
- data/lib/qless/pool/tasks.rb +20 -0
- data/lib/qless/pool/version.rb +5 -0
- data/man/qless-pool.1 +88 -0
- data/man/qless-pool.1.ronn +92 -0
- data/man/qless-pool.yml.5 +46 -0
- data/man/qless-pool.yml.5.ronn +41 -0
- data/spec/mock_config.rb +6 -0
- data/spec/qless-pool-custom.yml.erb +1 -0
- data/spec/qless-pool.yml +13 -0
- data/spec/qless_pool_spec.rb +166 -0
- data/spec/spec_helper.rb +3 -0
- metadata +213 -0
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'trollop'
|
2
|
+
require 'qless/pool'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module Qless
|
6
|
+
class Pool
|
7
|
+
module CLI
|
8
|
+
extend self
|
9
|
+
|
10
|
+
def run
|
11
|
+
opts = parse_options
|
12
|
+
daemonize if opts[:daemon]
|
13
|
+
manage_pidfile opts[:pidfile]
|
14
|
+
redirect opts
|
15
|
+
setup_environment opts
|
16
|
+
set_pool_options opts
|
17
|
+
start_pool
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse_options
|
21
|
+
opts = Trollop::options do
|
22
|
+
version "qless-pool #{VERSION} (c) nicholas a. evans"
|
23
|
+
banner <<-EOS
|
24
|
+
qless-pool is the best way to manage a group (pool) of qless workers
|
25
|
+
|
26
|
+
When daemonized, stdout and stderr default to qless-pool.stdxxx.log files in
|
27
|
+
the log directory and pidfile defaults to qless-pool.pid in the current dir.
|
28
|
+
|
29
|
+
Usage:
|
30
|
+
qless-pool [options]
|
31
|
+
where [options] are:
|
32
|
+
EOS
|
33
|
+
opt :config, "Alternate path to config file", :type => String, :short => "-c"
|
34
|
+
opt :appname, "Alternate appname", :type => String, :short => "-a"
|
35
|
+
opt :daemon, "Run as a background daemon", :default => false, :short => "-d"
|
36
|
+
opt :stdout, "Redirect stdout to logfile", :type => String, :short => '-o'
|
37
|
+
opt :stderr, "Redirect stderr to logfile", :type => String, :short => '-e'
|
38
|
+
opt :nosync, "Don't sync logfiles on every write"
|
39
|
+
opt :pidfile, "PID file location", :type => String, :short => "-p"
|
40
|
+
opt :environment, "Set RAILS_ENV/RACK_ENV/QLESS_ENV", :type => String, :short => "-E"
|
41
|
+
opt :term_graceful_wait, "On TERM signal, wait for workers to shut down gracefully"
|
42
|
+
opt :term_graceful, "On TERM signal, shut down workers gracefully"
|
43
|
+
opt :term_immediate, "On TERM signal, shut down workers immediately (default)"
|
44
|
+
end
|
45
|
+
if opts[:daemon]
|
46
|
+
opts[:stdout] ||= "log/qless-pool.stdout.log"
|
47
|
+
opts[:stderr] ||= "log/qless-pool.stderr.log"
|
48
|
+
opts[:pidfile] ||= "tmp/pids/qless-pool.pid"
|
49
|
+
end
|
50
|
+
opts
|
51
|
+
end
|
52
|
+
|
53
|
+
def daemonize
|
54
|
+
raise 'First fork failed' if (pid = fork) == -1
|
55
|
+
exit unless pid.nil?
|
56
|
+
Process.setsid
|
57
|
+
raise 'Second fork failed' if (pid = fork) == -1
|
58
|
+
exit unless pid.nil?
|
59
|
+
end
|
60
|
+
|
61
|
+
def manage_pidfile(pidfile)
|
62
|
+
return unless pidfile
|
63
|
+
pid = Process.pid
|
64
|
+
if File.exist? pidfile
|
65
|
+
if process_still_running? pidfile
|
66
|
+
raise "Pidfile already exists at #{pidfile} and process is still running."
|
67
|
+
else
|
68
|
+
File.delete pidfile
|
69
|
+
end
|
70
|
+
else
|
71
|
+
FileUtils.mkdir_p File.dirname(pidfile)
|
72
|
+
end
|
73
|
+
File.open pidfile, "w" do |f|
|
74
|
+
f.write pid
|
75
|
+
end
|
76
|
+
at_exit do
|
77
|
+
if Process.pid == pid
|
78
|
+
File.delete pidfile
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def process_still_running?(pidfile)
|
84
|
+
old_pid = open(pidfile).read.strip.to_i
|
85
|
+
Process.kill 0, old_pid
|
86
|
+
true
|
87
|
+
rescue Errno::ESRCH
|
88
|
+
false
|
89
|
+
rescue Errno::EPERM
|
90
|
+
true
|
91
|
+
rescue ::Exception => e
|
92
|
+
$stderr.puts "While checking if PID #{old_pid} is running, unexpected #{e.class}: #{e}"
|
93
|
+
true
|
94
|
+
end
|
95
|
+
|
96
|
+
def redirect(opts)
|
97
|
+
$stdin.reopen '/dev/null' if opts[:daemon]
|
98
|
+
# need to reopen as File, or else Qless::Pool::Logging.reopen_logs! won't work
|
99
|
+
out = File.new(opts[:stdout], "a") if opts[:stdout] && !opts[:stdout].empty?
|
100
|
+
err = File.new(opts[:stderr], "a") if opts[:stderr] && !opts[:stderr].empty?
|
101
|
+
$stdout.reopen out if out
|
102
|
+
$stderr.reopen err if err
|
103
|
+
$stdout.sync = $stderr.sync = true unless opts[:nosync]
|
104
|
+
end
|
105
|
+
|
106
|
+
# TODO: global variables are not the best way
|
107
|
+
def set_pool_options(opts)
|
108
|
+
if opts[:daemon]
|
109
|
+
Qless::Pool.handle_winch = true
|
110
|
+
end
|
111
|
+
if opts[:term_graceful_wait]
|
112
|
+
Qless::Pool.term_behavior = "graceful_worker_shutdown_and_wait"
|
113
|
+
elsif opts[:term_graceful]
|
114
|
+
Qless::Pool.term_behavior = "graceful_worker_shutdown"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def setup_environment(opts)
|
119
|
+
Qless::Pool.app_name = opts[:appname] if opts[:appname]
|
120
|
+
ENV["RACK_ENV"] = ENV["RAILS_ENV"] = ENV["QLESS_ENV"] = opts[:environment] if opts[:environment]
|
121
|
+
Qless::Pool.log "Qless Pool running in #{ENV["RAILS_ENV"] || "development"} environment"
|
122
|
+
ENV["QLESS_POOL_CONFIG"] = opts[:config] if opts[:config]
|
123
|
+
end
|
124
|
+
|
125
|
+
def start_pool
|
126
|
+
require 'rake'
|
127
|
+
require 'qless/pool/tasks'
|
128
|
+
Rake.application.init
|
129
|
+
Rake.application.load_rakefile
|
130
|
+
Rake.application["qless:pool"].invoke
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Qless
|
2
|
+
class Pool
|
3
|
+
module Logging
|
4
|
+
extend self
|
5
|
+
|
6
|
+
# more than a little bit complicated...
|
7
|
+
# copied this from Unicorn.
|
8
|
+
def self.reopen_logs!
|
9
|
+
log "Flushing logs"
|
10
|
+
[$stdout, $stderr].each do |fd|
|
11
|
+
if fd.instance_of? File
|
12
|
+
# skip if the file is the exact same inode and device
|
13
|
+
orig_st = fd.stat
|
14
|
+
begin
|
15
|
+
cur_st = File.stat(fd.path)
|
16
|
+
next if orig_st.ino == cur_st.ino && orig_st.dev == cur_st.dev
|
17
|
+
rescue Errno::ENOENT
|
18
|
+
end
|
19
|
+
# match up the encoding
|
20
|
+
open_arg = 'a'
|
21
|
+
if fd.respond_to?(:external_encoding) && enc = fd.external_encoding
|
22
|
+
open_arg << ":#{enc.to_s}"
|
23
|
+
enc = fd.internal_encoding and open_arg << ":#{enc.to_s}"
|
24
|
+
end
|
25
|
+
# match up buffering (does reopen reset this?)
|
26
|
+
sync = fd.sync
|
27
|
+
# sync to disk
|
28
|
+
fd.fsync
|
29
|
+
# reopen, and set ruby buffering appropriately
|
30
|
+
fd.reopen fd.path, open_arg
|
31
|
+
fd.sync = sync
|
32
|
+
log "Reopened logfile: #{fd.path}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Given a string, sets the procline ($0)
|
38
|
+
# Procline is always in the format of:
|
39
|
+
# qless-pool-master: STRING
|
40
|
+
def procline(string)
|
41
|
+
$0 = "qless-pool-master#{app}: #{string}"
|
42
|
+
end
|
43
|
+
|
44
|
+
# TODO: make this use an actual logger
|
45
|
+
def log(message)
|
46
|
+
puts "qless-pool-manager#{app}[#{Process.pid}]: #{message}"
|
47
|
+
#$stdout.fsync
|
48
|
+
end
|
49
|
+
|
50
|
+
# TODO: make this use an actual logger
|
51
|
+
def log_worker(message)
|
52
|
+
puts "qless-pool-worker#{app}[#{Process.pid}]: #{message}"
|
53
|
+
#$stdout.fsync
|
54
|
+
end
|
55
|
+
|
56
|
+
# Include optional app name in procline
|
57
|
+
def app
|
58
|
+
app_name = self.respond_to?(:app_name) && self.app_name
|
59
|
+
app_name ||= self.class.respond_to?(:app_name) && self.class.app_name
|
60
|
+
app_name ? "[#{app_name}]" : ""
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Qless
|
2
|
+
class PoolFactory
|
3
|
+
|
4
|
+
def initialize(options={})
|
5
|
+
@options = {
|
6
|
+
:term_timeout => ENV['TERM_TIMEOUT'] || 4.0,
|
7
|
+
:verbose => !!ENV['VERBOSE'],
|
8
|
+
:very_verbose => !!ENV['VVERBOSE'],
|
9
|
+
:run_as_single_process => !!ENV['RUN_AS_SINGLE_PROCESS']
|
10
|
+
}.merge(options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def client
|
14
|
+
@qless_client ||= Qless::Client.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def client=(client)
|
18
|
+
@qless_client = client
|
19
|
+
end
|
20
|
+
|
21
|
+
def reserver_class
|
22
|
+
@reserver_class ||= Qless::JobReservers.const_get(ENV.fetch('JOB_RESERVER', 'Ordered'))
|
23
|
+
end
|
24
|
+
|
25
|
+
def reserver_class=(reserver_class)
|
26
|
+
@reserver_class = reserver_class
|
27
|
+
end
|
28
|
+
|
29
|
+
def reserver(queues)
|
30
|
+
reserver_class.new(queues)
|
31
|
+
end
|
32
|
+
|
33
|
+
def worker(queues)
|
34
|
+
queues = queues.to_s.split(',').map { |q| client.queues[q.strip] }
|
35
|
+
if queues.none?
|
36
|
+
raise "No queues provided"
|
37
|
+
end
|
38
|
+
|
39
|
+
Qless::Worker.new(reserver(queues), @options)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'qless/worker'
|
2
|
+
|
3
|
+
class Qless::Pool
|
4
|
+
module PooledWorker
|
5
|
+
def shutdown_with_pool
|
6
|
+
shutdown_without_pool || Process.ppid == 1
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.included(base)
|
10
|
+
base.instance_eval do
|
11
|
+
alias_method :shutdown_without_pool, :shutdown?
|
12
|
+
alias_method :shutdown?, :shutdown_with_pool
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Qless::Worker.class_eval do
|
20
|
+
include Qless::Pool::PooledWorker
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'qless/tasks'
|
3
|
+
|
4
|
+
namespace :qless do
|
5
|
+
|
6
|
+
# qless worker config (not pool related). e.g. hoptoad, rails environment
|
7
|
+
task :setup
|
8
|
+
|
9
|
+
namespace :pool do
|
10
|
+
# qless pool config. e.g. after_prefork connection handling
|
11
|
+
task :setup
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "Launch a pool of qless workers"
|
15
|
+
task :pool => %w[qless:setup qless:pool:setup] do
|
16
|
+
require 'qless/pool'
|
17
|
+
Qless::Pool.run
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
data/man/qless-pool.1
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
.\" generated with Ronn/v0.7.3
|
2
|
+
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
3
|
+
.
|
4
|
+
.TH "QLESS\-POOL" "1" "June 2012" "QLESS-POOL 0.4.0.DEV" "QLESS-POOL"
|
5
|
+
.
|
6
|
+
.SH "NAME"
|
7
|
+
\fBqless\-pool\fR \- qless worker pool management
|
8
|
+
.
|
9
|
+
.SH "SYNOPSIS"
|
10
|
+
\fBqless\-pool\fR \fIoptions\fR
|
11
|
+
.
|
12
|
+
.SH "DESCRIPTION"
|
13
|
+
\fBQless\-pool\fR is the best way to manage a group (pool) of qless workers\.
|
14
|
+
.
|
15
|
+
.P
|
16
|
+
When qless\-pool(1) is daemonized the \fBstdout\fR and \fBstderr\fR output streams are redirected to \fBqless\-pool\.stdxxx\.log\fR log files in the \fBlog\fR directory\. Additionally the PID file defaults to \fBqless\-pool\.pid\fR in the \fBtmp/pids\fR directory\.
|
17
|
+
.
|
18
|
+
.SH "OPTIONS"
|
19
|
+
.
|
20
|
+
.IP "\(bu" 4
|
21
|
+
\fB\-c, \-\-config\fR \fIfile\fR: Uses the configuration specified in the \fIfile\fR provided instead of searching in the current and \fBconfig\fR directories for \fBqless\-pool\.yml\fR\.
|
22
|
+
.
|
23
|
+
.IP "\(bu" 4
|
24
|
+
\fB\-a, \-\-appname\fR \fIname\fR: Specifies the app name to be used for logging and procline\. If not specified, this defaults to the current working directory\.
|
25
|
+
.
|
26
|
+
.IP "\(bu" 4
|
27
|
+
\fB\-d, \-\-daemon\fR: Runs \fBqless\-pool\fR in the background as a daemon process\. This will redirect \fBstdout\fR and \fBstderr\fR to log files and write a PID file\.
|
28
|
+
.
|
29
|
+
.IP "\(bu" 4
|
30
|
+
\fB\-o, \-\-stdout\fR \fIfile\fR: Writes the normal log output to \fIfile\fR instead of printing to the terminal\. When running as a daemon this defaults to the path \fBlog/qless\-pool\.stdout\.log\fR\.
|
31
|
+
.
|
32
|
+
.IP "\(bu" 4
|
33
|
+
\fB\-e, \-\-stderr\fR \fIfile\fR: Writes the standard error output to \fIfile\fR instead of printing to the terminal\. When running as a daemon this defaults to the path \fBlog/qless\-pool\.stderr\.log\fR\.
|
34
|
+
.
|
35
|
+
.IP "\(bu" 4
|
36
|
+
\fB\-\-nosync\fR Allows writes to \fBstdout\fR and \fBstderr\fR to be buffered\.
|
37
|
+
.
|
38
|
+
.IP "\(bu" 4
|
39
|
+
\fB\-p, \-\-pidfile\fR \fIfile\fR: Writes the PID to the \fIfile\fR\. When running as a daemon this defaults to \fBtmp/pids/qless\-pool\.pid\fR\.
|
40
|
+
.
|
41
|
+
.IP "\(bu" 4
|
42
|
+
\fB\-E, \-\-environment\fR \fIname\fR: Specifies the environment \fIname\fR to be set for \fBRAILS_ENV\fR, \fBRACK_ENV\fR and \fBQLESS_ENV\fR which will be passed on to the pooled qless workers\.
|
43
|
+
.
|
44
|
+
.IP "\(bu" 4
|
45
|
+
\fB\-\-term\-graceful\-wait\fR: Configure TERM signal handling so the master will gracefully request worker shut downs (via \fBQUIT\fR) and wait for the workers to quit before shutting down itself\. This is the same behavior as the \fBQUIT\fR signal\.
|
46
|
+
.
|
47
|
+
.IP "\(bu" 4
|
48
|
+
\fB\-\-term\-graceful\fR: Configure TERM signal handling so the master will gracefully request worker shut downs (via \fBQUIT\fR) but shut itself down immediately\. This the same behavior as the \fBINT\fR signal\.
|
49
|
+
.
|
50
|
+
.IP "\(bu" 4
|
51
|
+
\fB\-\-term\-immediate\fR: Configure TERM signal handling so the master will request imediate worker shut downs (via \fBINT\fR) and shut itself down immediately\. This is the default \fBTERM\fR signal behavior\.
|
52
|
+
.
|
53
|
+
.IP "" 0
|
54
|
+
.
|
55
|
+
.SH "HISTORY"
|
56
|
+
.
|
57
|
+
.TP
|
58
|
+
\fBv0\.3\.0\fR
|
59
|
+
Support for ruby 1\.9, qless 1\.20\.
|
60
|
+
.
|
61
|
+
.br
|
62
|
+
Added appname for logging\.
|
63
|
+
.
|
64
|
+
.br
|
65
|
+
Minor bugfixes\.
|
66
|
+
.
|
67
|
+
.TP
|
68
|
+
\fBv0\.2\.0\fR
|
69
|
+
Support for reloading logs and workers with \fBHUP\fR signal
|
70
|
+
.
|
71
|
+
.br
|
72
|
+
Cleans up PID file on startup
|
73
|
+
.
|
74
|
+
.br
|
75
|
+
Fixed \fB\-c, \-\-config\fR option\.
|
76
|
+
.
|
77
|
+
.TP
|
78
|
+
\fBv0\.1\.0\fR
|
79
|
+
\fBqless\-pool\fR command line interface added
|
80
|
+
.
|
81
|
+
.SH "AUTHOR"
|
82
|
+
Nicholas Evans
|
83
|
+
.
|
84
|
+
.SH "COPYRIGHT"
|
85
|
+
Copyright (C) 2010 by Nicholas Evans \fInick@ekenosen\.net\fR, et al\.
|
86
|
+
.
|
87
|
+
.SH "SEE ALSO"
|
88
|
+
qless\-pool\.yml(5)
|
@@ -0,0 +1,92 @@
|
|
1
|
+
qless-pool(1) -- qless worker pool management
|
2
|
+
================================================
|
3
|
+
|
4
|
+
## SYNOPSIS
|
5
|
+
|
6
|
+
`qless-pool` [options]
|
7
|
+
|
8
|
+
## DESCRIPTION
|
9
|
+
|
10
|
+
**Qless-pool** is the best way to manage a group (pool) of qless workers.
|
11
|
+
|
12
|
+
When qless-pool(1) is daemonized the `stdout` and `stderr` output streams
|
13
|
+
are redirected to `qless-pool.stdxxx.log` log files in the `log` directory.
|
14
|
+
Additionally the PID file defaults to `qless-pool.pid` in the `tmp/pids`
|
15
|
+
directory.
|
16
|
+
|
17
|
+
## OPTIONS
|
18
|
+
|
19
|
+
* `-c, --config` <file>:
|
20
|
+
Uses the configuration specified in the <file> provided instead of
|
21
|
+
searching in the current and `config` directories for `qless-pool.yml`.
|
22
|
+
|
23
|
+
* `-a, --appname` <name>:
|
24
|
+
Specifies the app name to be used for logging and procline. If not
|
25
|
+
specified, this defaults to the current working directory.
|
26
|
+
|
27
|
+
* `-d, --daemon`:
|
28
|
+
Runs `qless-pool` in the background as a daemon process. This will
|
29
|
+
redirect `stdout` and `stderr` to log files and write a PID file.
|
30
|
+
|
31
|
+
* `-o, --stdout` <file>:
|
32
|
+
Writes the normal log output to <file> instead of printing to the
|
33
|
+
terminal. When running as a daemon this defaults to the path
|
34
|
+
`log/qless-pool.stdout.log`.
|
35
|
+
|
36
|
+
* `-e, --stderr` <file>:
|
37
|
+
Writes the standard error output to <file> instead of printing to the
|
38
|
+
terminal. When running as a daemon this defaults to the path
|
39
|
+
`log/qless-pool.stderr.log`.
|
40
|
+
|
41
|
+
* `--nosync`
|
42
|
+
Allows writes to `stdout` and `stderr` to be buffered.
|
43
|
+
|
44
|
+
* `-p, --pidfile` <file>:
|
45
|
+
Writes the PID to the <file>. When running as a daemon this defaults
|
46
|
+
to `tmp/pids/qless-pool.pid`.
|
47
|
+
|
48
|
+
* `-E, --environment` <name>:
|
49
|
+
Specifies the environment <name> to be set for `RAILS_ENV`, `RACK_ENV`
|
50
|
+
and `QLESS_ENV` which will be passed on to the pooled qless workers.
|
51
|
+
|
52
|
+
* `--term-graceful-wait`:
|
53
|
+
Configure TERM signal handling so the master will gracefully request worker
|
54
|
+
shut downs (via `QUIT`) and wait for the workers to quit before shutting
|
55
|
+
down itself. This is the same behavior as the `QUIT` signal.
|
56
|
+
|
57
|
+
* `--term-graceful`:
|
58
|
+
Configure TERM signal handling so the master will gracefully request worker
|
59
|
+
shut downs (via `QUIT`) but shut itself down immediately. This the same
|
60
|
+
behavior as the `INT` signal.
|
61
|
+
|
62
|
+
* `--term-immediate`:
|
63
|
+
Configure TERM signal handling so the master will request imediate worker
|
64
|
+
shut downs (via `INT`) and shut itself down immediately. This is the
|
65
|
+
default `TERM` signal behavior.
|
66
|
+
|
67
|
+
## HISTORY
|
68
|
+
|
69
|
+
* `v0.3.0`:
|
70
|
+
Support for ruby 1.9, qless 1.20.<br>
|
71
|
+
Added appname for logging.<br>
|
72
|
+
Minor bugfixes.
|
73
|
+
|
74
|
+
* `v0.2.0`:
|
75
|
+
Support for reloading logs and workers with `HUP` signal<br>
|
76
|
+
Cleans up PID file on startup<br>
|
77
|
+
Fixed `-c, --config` option.
|
78
|
+
|
79
|
+
* `v0.1.0`:
|
80
|
+
`qless-pool` command line interface added
|
81
|
+
|
82
|
+
## AUTHOR
|
83
|
+
|
84
|
+
Nicholas Evans
|
85
|
+
|
86
|
+
## COPYRIGHT
|
87
|
+
|
88
|
+
Copyright (C) 2010 by Nicholas Evans <nick@ekenosen.net>, et al.
|
89
|
+
|
90
|
+
## SEE ALSO
|
91
|
+
|
92
|
+
qless-pool.yml(5)
|