mongrel_cow_cluster 0.3.5
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.
- data/CHANGELOG +5 -0
- data/LICENSE +56 -0
- data/Manifest +7 -0
- data/README +133 -0
- data/lib/mongrel_cow_cluster/init.rb +437 -0
- data/mongrel_cow_cluster.gemspec +47 -0
- data/resources/defaults.yaml +2 -0
- data/tools/rakehelp.rb +123 -0
- metadata +71 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
v0.3.5 fix default timeout to match Mongrel default; respect "num_procs" for mongrel_cluster compatibility
|
2
|
+
v0.3.4 load configuration file before validation
|
3
|
+
v0.3.3 respect "address" in config for mongrel_cluster compatibility
|
4
|
+
v0.3.1 bind to a random port on 127.0.0.1 to preload Rails
|
5
|
+
v0.3.0 rolling_interval option added to delay between member restarts
|
data/LICENSE
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
cow_cluster is copyrighted free software by Eric Wong <normalperson@yhbt.net>.
|
2
|
+
You can redistribute it and/or modify it under either the terms of the GPL
|
3
|
+
version 2 (see the file GPL), or the conditions below:
|
4
|
+
|
5
|
+
1. You may make and give away verbatim copies of the source form of the
|
6
|
+
software without restriction, provided that you duplicate all of the
|
7
|
+
original copyright notices and associated disclaimers.
|
8
|
+
|
9
|
+
2. You may modify your copy of the software in any way, provided that
|
10
|
+
you do at least ONE of the following:
|
11
|
+
|
12
|
+
a) place your modifications in the Public Domain or otherwise
|
13
|
+
make them Freely Available, such as by posting said
|
14
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
15
|
+
the author to include your modifications in the software.
|
16
|
+
|
17
|
+
b) use the modified software only within your corporation or
|
18
|
+
organization.
|
19
|
+
|
20
|
+
c) give non-standard binaries non-standard names, with
|
21
|
+
instructions on where to get the original software distribution.
|
22
|
+
|
23
|
+
d) make other distribution arrangements with the author.
|
24
|
+
|
25
|
+
3. You may distribute the software in object code or binary form,
|
26
|
+
provided that you do at least ONE of the following:
|
27
|
+
|
28
|
+
a) distribute the binaries and library files of the software,
|
29
|
+
together with instructions (in the manual page or equivalent)
|
30
|
+
on where to get the original distribution.
|
31
|
+
|
32
|
+
b) accompany the distribution with the machine-readable source of
|
33
|
+
the software.
|
34
|
+
|
35
|
+
c) give non-standard binaries non-standard names, with
|
36
|
+
instructions on where to get the original software distribution.
|
37
|
+
|
38
|
+
d) make other distribution arrangements with the author.
|
39
|
+
|
40
|
+
4. You may modify and include the part of the software into any other
|
41
|
+
software (possibly commercial). But some files in the distribution
|
42
|
+
are not written by the author, so that they are not under these terms.
|
43
|
+
|
44
|
+
For the list of those files and their copying conditions, see the
|
45
|
+
file LEGAL.
|
46
|
+
|
47
|
+
5. The scripts and library files supplied as input to or produced as
|
48
|
+
output from the software do not automatically fall under the
|
49
|
+
copyright of the software, but belong to whomever generated them,
|
50
|
+
and may be sold commercially, and may be aggregated with this
|
51
|
+
software.
|
52
|
+
|
53
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
54
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
55
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
56
|
+
PURPOSE.
|
data/Manifest
ADDED
data/README
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
MongrelCowCluster
|
2
|
+
by Eric Wong <normalperson@yhbt.net>
|
3
|
+
FIX (http://rubyforge.org/projects/mongrel-cow/)
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
A faster, Rails-only replacement for mongrel_cluster
|
8
|
+
|
9
|
+
Uses copy-on-write fork() to drastically reduce startup times when
|
10
|
+
starting mongrel server. This means all of the Rails and environment
|
11
|
+
code is read and loaded _once_ and forked N times where N is the number
|
12
|
+
of ports one wishes to bind to.
|
13
|
+
|
14
|
+
It is very similar to mongrel_cluster in use and can use config files
|
15
|
+
generated for mongrel_cluster. This still creates pid and log files
|
16
|
+
that are similar to those used by mongrel_cluster (mongrel.<port>.log
|
17
|
+
and mongrel.<port>.pid).
|
18
|
+
|
19
|
+
All children are independent of each other and the death of one will
|
20
|
+
not influence another. The parent process dies as soon as the final
|
21
|
+
child is spawned.
|
22
|
+
|
23
|
+
Unlike traditional non-Ruby applications that use copy-on-write fork()
|
24
|
+
to save memory, do NOT expect any significant memory savings even though
|
25
|
+
this uses copy-on-write(), since the Ruby GC will trigger writes on
|
26
|
+
whatever pages it marks. The main performance benefit of this is that
|
27
|
+
the application code can be parsed once and reused.
|
28
|
+
|
29
|
+
If you connect to any other databases or services at Rails startup, you
|
30
|
+
will need to re-init them manually. The default ActiveRecord::Base
|
31
|
+
connection provided by Rails is disconnected after Rails is loaded
|
32
|
+
and restarted/reconnected in each child process.
|
33
|
+
|
34
|
+
|
35
|
+
== FEATURES/PROBLEMS:
|
36
|
+
|
37
|
+
Rolling restarts:
|
38
|
+
|
39
|
+
Although machines are all issued cow_cluster restart commands at
|
40
|
+
the same time (by Capistrano), each individual mongrel process
|
41
|
+
will be restarted independently of the others. This means that
|
42
|
+
(assuming this works correctly :), only ONE mongrel process
|
43
|
+
per machine will be down at any given time. This makes life
|
44
|
+
a lot easier for the Apache proxies (which will failover to
|
45
|
+
the next available port) and *should* be completely transparent
|
46
|
+
to the end user.
|
47
|
+
|
48
|
+
We now also check for successful startup of each individual port
|
49
|
+
by issuing an HTTP GET on "/" and report back to the person
|
50
|
+
running cow_cluster if that server has been successfully
|
51
|
+
started.
|
52
|
+
|
53
|
+
Daemonization is handled internally by cow_cluster since
|
54
|
+
Daemons::Daemonize (used by Mongrel) does too many things behind
|
55
|
+
our back wrt closing AR:B connections and redirecting log
|
56
|
+
files of the managing process.
|
57
|
+
|
58
|
+
== SYNOPSIS:
|
59
|
+
|
60
|
+
Start:
|
61
|
+
mongrel_rails cow_cluster::start -C config/mongrel_cluster.yml
|
62
|
+
Stop:
|
63
|
+
mongrel_rails cow_cluster::stop -C config/mongrel_cluster.yml
|
64
|
+
Restart:
|
65
|
+
mongrel_rails cow_cluster::restart -C config/mongrel_cluster.yml
|
66
|
+
|
67
|
+
== REQUIREMENTS:
|
68
|
+
|
69
|
+
gem_plugin >= 0.2.2
|
70
|
+
mongrel >= 1.0.1
|
71
|
+
|
72
|
+
== INSTALL:
|
73
|
+
|
74
|
+
sudo gem install mongrel_cow_cluster
|
75
|
+
|
76
|
+
== LICENSE:
|
77
|
+
|
78
|
+
cow_cluster is copyrighted free software by Eric Wong <normalperson@yhbt.net>.
|
79
|
+
You can redistribute it and/or modify it under either the terms of the GPL
|
80
|
+
version 2 (see the file GPL), or the conditions below:
|
81
|
+
|
82
|
+
1. You may make and give away verbatim copies of the source form of the
|
83
|
+
software without restriction, provided that you duplicate all of the
|
84
|
+
original copyright notices and associated disclaimers.
|
85
|
+
|
86
|
+
2. You may modify your copy of the software in any way, provided that
|
87
|
+
you do at least ONE of the following:
|
88
|
+
|
89
|
+
a) place your modifications in the Public Domain or otherwise
|
90
|
+
make them Freely Available, such as by posting said
|
91
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
92
|
+
the author to include your modifications in the software.
|
93
|
+
|
94
|
+
b) use the modified software only within your corporation or
|
95
|
+
organization.
|
96
|
+
|
97
|
+
c) give non-standard binaries non-standard names, with
|
98
|
+
instructions on where to get the original software distribution.
|
99
|
+
|
100
|
+
d) make other distribution arrangements with the author.
|
101
|
+
|
102
|
+
3. You may distribute the software in object code or binary form,
|
103
|
+
provided that you do at least ONE of the following:
|
104
|
+
|
105
|
+
a) distribute the binaries and library files of the software,
|
106
|
+
together with instructions (in the manual page or equivalent)
|
107
|
+
on where to get the original distribution.
|
108
|
+
|
109
|
+
b) accompany the distribution with the machine-readable source of
|
110
|
+
the software.
|
111
|
+
|
112
|
+
c) give non-standard binaries non-standard names, with
|
113
|
+
instructions on where to get the original software distribution.
|
114
|
+
|
115
|
+
d) make other distribution arrangements with the author.
|
116
|
+
|
117
|
+
4. You may modify and include the part of the software into any other
|
118
|
+
software (possibly commercial). But some files in the distribution
|
119
|
+
are not written by the author, so that they are not under these terms.
|
120
|
+
|
121
|
+
For the list of those files and their copying conditions, see the
|
122
|
+
file LEGAL.
|
123
|
+
|
124
|
+
5. The scripts and library files supplied as input to or produced as
|
125
|
+
output from the software do not automatically fall under the
|
126
|
+
copyright of the software, but belong to whomever generated them,
|
127
|
+
and may be sold commercially, and may be aggregated with this
|
128
|
+
software.
|
129
|
+
|
130
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
131
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
132
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
133
|
+
PURPOSE.
|
@@ -0,0 +1,437 @@
|
|
1
|
+
# Copyright (c) 2007 Eric Wong
|
2
|
+
# Based on code from Mongrel, Copyright (c) 2005 Zed A. Shaw
|
3
|
+
#
|
4
|
+
# You can redistribute it and/or modify it under the same terms as Ruby.
|
5
|
+
#
|
6
|
+
# Additional work donated by contributors.
|
7
|
+
# See http://mongrel.rubyforge.org/attributions.html for more information.
|
8
|
+
|
9
|
+
require 'gem_plugin'
|
10
|
+
require 'mongrel'
|
11
|
+
require 'net/http'
|
12
|
+
|
13
|
+
module Cow_Cluster
|
14
|
+
module Base
|
15
|
+
|
16
|
+
def read_options(settings)
|
17
|
+
if @config_file
|
18
|
+
settings.merge!(config_settings = YAML.load_file(@config_file))
|
19
|
+
# symbolize keys, mongrel_cluster uses string keys :(
|
20
|
+
keys = settings.keys.collect { |k| k.kind_of?(Symbol) ? nil : k }
|
21
|
+
keys.compact.each { |k| settings[k.to_sym] = settings.delete(k) }
|
22
|
+
STDERR.puts "** Loading settings from #{@config_file} " +
|
23
|
+
"(they override command line)."
|
24
|
+
if settings[:address] && ! config_settings[:host]
|
25
|
+
settings[:host] = settings[:address]
|
26
|
+
end
|
27
|
+
settings.each { |k,v| instance_variable_set("@#{k.to_s}", v) }
|
28
|
+
end
|
29
|
+
|
30
|
+
@file = { :pid => {}, :log => {} }
|
31
|
+
@file.keys.each { |f|
|
32
|
+
x = "#{f.to_s}_file".to_sym
|
33
|
+
next unless settings[x]
|
34
|
+
@file[f][:ext] = File.extname(settings[x])
|
35
|
+
@file[f][:base] = File.basename(settings[x], @file[f][:ext])
|
36
|
+
@file[f][:dir] = File.dirname(settings[x])
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def each_port(settings, &block)
|
41
|
+
if settings[:only]
|
42
|
+
yield settings[:only]
|
43
|
+
else
|
44
|
+
max_port = settings[:port] + settings[:servers] - 1
|
45
|
+
(settings[:port]..max_port).each { |port|
|
46
|
+
yield port
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def port_pid_file(port)
|
52
|
+
base = "#{@file[:pid][:base]}.#{port}#{@file[:pid][:ext]}"
|
53
|
+
File.join(@file[:pid][:dir], base)
|
54
|
+
end
|
55
|
+
|
56
|
+
def port_log_file(port)
|
57
|
+
base = "#{@file[:log][:base]}.#{port}#{@file[:log][:ext]}"
|
58
|
+
File.join(@file[:log][:dir], base)
|
59
|
+
end
|
60
|
+
|
61
|
+
def stop_options
|
62
|
+
[
|
63
|
+
['-c', '--chdir PATH',
|
64
|
+
"Change to dir before starting (will be expanded)", :@cwd, Dir.pwd],
|
65
|
+
['-C', '--config PATH', "Use a config file", :@config_file, nil],
|
66
|
+
['-f', '--force', "Force the shutdown (kill -9).", :@force, false],
|
67
|
+
['-o', '--only INT', 'Limit operation to only one port', :@only, nil ],
|
68
|
+
['-w', '--wait SECONDS', "Wait SECONDS before forcing shutdown",
|
69
|
+
:@wait, "10"],
|
70
|
+
['-p', '--port INT', "First port to bind to", :@port, 3000],
|
71
|
+
['-s', '--servers INT', "Number of servers to start", :@servers, 2],
|
72
|
+
['-P', '--pid FILE', "Where to write the PID", :@pid_file,
|
73
|
+
"log/mongrel.pid"],
|
74
|
+
]
|
75
|
+
end
|
76
|
+
|
77
|
+
def validate_stop_options
|
78
|
+
if @config_file
|
79
|
+
valid_exists? @config_file, "Config file not there: #@config_file"
|
80
|
+
load_settings
|
81
|
+
end
|
82
|
+
@cwd = File.expand_path(@cwd)
|
83
|
+
valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd"
|
84
|
+
Dir.chdir @cwd
|
85
|
+
|
86
|
+
@valid
|
87
|
+
end
|
88
|
+
|
89
|
+
def start_options(restart = false)
|
90
|
+
ret = [
|
91
|
+
["-e", "--environment ENV", "Rails environment to run as",
|
92
|
+
:@environment, ENV['RAILS_ENV'] || "development"],
|
93
|
+
['-p', '--port INT', "First port to bind to", :@port, 3000],
|
94
|
+
['-s', '--servers INT', "Number of servers to start", :@servers, 2],
|
95
|
+
['-a', '--address ADDR', "Address to bind to", :@address, "0.0.0.0"],
|
96
|
+
['-l', '--log FILE', "Where to write log messages", :@log_file,
|
97
|
+
"log/mongrel.log"],
|
98
|
+
['-P', '--pid FILE', "Where to write the PID", :@pid_file,
|
99
|
+
"log/mongrel.pid"],
|
100
|
+
['-n', '--num-procs INT',
|
101
|
+
"Number of processors active before clients denied",
|
102
|
+
:@num_procs, 1024],
|
103
|
+
['-t', '--timeout TIME',
|
104
|
+
"Timeout all requests after seconds time", :@timeout, 60],
|
105
|
+
['-m', '--mime PATH', "A YAML file that lists additional MIME types",
|
106
|
+
:@mime_map, nil],
|
107
|
+
['-c', '--chdir PATH',
|
108
|
+
"Change to dir before starting (will be expanded)", :@cwd, Dir.pwd],
|
109
|
+
['-r', '--root PATH', "Set the document root (default 'public')",
|
110
|
+
:@docroot, "public"],
|
111
|
+
['-B', '--debug', "Enable debugging mode", :@debug, false],
|
112
|
+
['-C', '--config PATH', "Use a config file", :@config_file, nil],
|
113
|
+
['-S', '--script PATH',
|
114
|
+
"Load the given file as an extra config script",
|
115
|
+
:@config_script, nil],
|
116
|
+
['-o', '--only INT', 'Limit operation to only one port', :@only, nil ],
|
117
|
+
['', '--user USER', "User to run as", :@user, nil],
|
118
|
+
['', '--group GROUP', "Group to run as", :@group, nil],
|
119
|
+
['', '--prefix PATH', "URL prefix for Rails app", :@prefix, nil]
|
120
|
+
]
|
121
|
+
if restart
|
122
|
+
seen = {}
|
123
|
+
ret.each { |o| seen[o[1]] = true }
|
124
|
+
stop_options.each { |o|
|
125
|
+
ret << o unless seen[o[1]]
|
126
|
+
}
|
127
|
+
ret << [ '-i', '--rolling-interval SECONDS',
|
128
|
+
"Seconds to delay between each child restart",
|
129
|
+
:@rolling_interval, nil]
|
130
|
+
end
|
131
|
+
ret
|
132
|
+
end
|
133
|
+
|
134
|
+
def validate_start_options
|
135
|
+
if @config_file
|
136
|
+
valid_exists? @config_file, "Config file not there: #@config_file"
|
137
|
+
load_settings
|
138
|
+
end
|
139
|
+
@cwd = File.expand_path(@cwd)
|
140
|
+
valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd"
|
141
|
+
|
142
|
+
# Change there to start, then we'll have to come back after daemonize
|
143
|
+
Dir.chdir(@cwd)
|
144
|
+
|
145
|
+
valid?(@prefix[0].chr == "/" && @prefix[-1].chr != "/",
|
146
|
+
"Prefix must begin with / and not end in /") if @prefix
|
147
|
+
valid_dir? File.dirname(@log_file),
|
148
|
+
"Path to log file not valid: #@log_file"
|
149
|
+
valid_dir? File.dirname(@pid_file),
|
150
|
+
"Path to pid file not valid: #@pid_file"
|
151
|
+
valid_dir? @docroot, "Path to docroot not valid: #@docroot"
|
152
|
+
valid_exists? @mime_map, "MIME mapping file does not exist: #@mime_map" \
|
153
|
+
if @mime_map
|
154
|
+
valid_user? @user if @user
|
155
|
+
valid_group? @group if @group
|
156
|
+
|
157
|
+
return @valid
|
158
|
+
end
|
159
|
+
|
160
|
+
def stop_child(settings, port, force = @force)
|
161
|
+
pid_file = port_pid_file(port)
|
162
|
+
|
163
|
+
if File.exist?(pid_file)
|
164
|
+
signal = force ? 'KILL' : 'TERM'
|
165
|
+
pid = File.open(pid_file).read.to_i
|
166
|
+
STDERR.print "Sending #{signal} to Mongrel at PID #{pid}..."
|
167
|
+
begin
|
168
|
+
Process.kill(signal, pid)
|
169
|
+
rescue Errno::ESRCH
|
170
|
+
STDERR.puts 'Process does not exist. Not running.'
|
171
|
+
File.unlink(pid_file)
|
172
|
+
end
|
173
|
+
if force
|
174
|
+
File.unlink(pid_file) rescue nil
|
175
|
+
else
|
176
|
+
@wait.to_i.times { |nr| sleep(1) if File.exist?(pid_file) }
|
177
|
+
end
|
178
|
+
STDERR.puts 'Done.'
|
179
|
+
else
|
180
|
+
STDERR.puts "#{pid_file} not found, skipping."
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def load_settings
|
185
|
+
# Config file settings will override command line settings
|
186
|
+
settings = { :host => @address, :port => @port, :cwd => @cwd,
|
187
|
+
:log_file => @log_file, :pid_file => @pid_file,
|
188
|
+
:environment => @environment,
|
189
|
+
:docroot => @docroot, :mime_map => @mime_map,
|
190
|
+
:debug => @debug, :includes => ["mongrel"],
|
191
|
+
:config_script => @config_script,
|
192
|
+
:num_processors => @num_procs, :timeout => @timeout,
|
193
|
+
:servers => @servers,
|
194
|
+
:user => @user, :group => @group, :prefix => @prefix,
|
195
|
+
:only => @only, :rolling_interval => @rolling_interval,
|
196
|
+
:config_file => @config_file
|
197
|
+
}
|
198
|
+
read_options(settings)
|
199
|
+
settings
|
200
|
+
end
|
201
|
+
|
202
|
+
def restart_run(restart = false)
|
203
|
+
settings = load_settings
|
204
|
+
settings.merge(:force => @force, :wait => @wait) if restart
|
205
|
+
config = rails_preload(settings)
|
206
|
+
ObjectSpace.each_object(TCPServer) { |x| x.close rescue nil }
|
207
|
+
|
208
|
+
close_rails_connections
|
209
|
+
|
210
|
+
pid = fork {
|
211
|
+
File.umask 0000
|
212
|
+
Process.setsid
|
213
|
+
trap 'HUP', 'IGNORE'
|
214
|
+
Dir.chdir(@cwd)
|
215
|
+
ri = nil
|
216
|
+
each_port(settings) { |port|
|
217
|
+
if restart && settings[:rolling_interval]
|
218
|
+
if ri
|
219
|
+
STDERR.puts "Delaying next restart by #{ri} seconds " +
|
220
|
+
"(rolling-interval)"
|
221
|
+
sleep(ri)
|
222
|
+
else
|
223
|
+
ri = settings[:rolling_interval].to_f
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
GC.start
|
228
|
+
if restart
|
229
|
+
stop_child(settings, port)
|
230
|
+
GC.start
|
231
|
+
end
|
232
|
+
|
233
|
+
spawn_child(settings, port)
|
234
|
+
}
|
235
|
+
}
|
236
|
+
Process.waitpid(pid)
|
237
|
+
exit 0
|
238
|
+
end # restart_run
|
239
|
+
|
240
|
+
def write_child_pid(settings)
|
241
|
+
pid_file = settings[:pid_file]
|
242
|
+
if File.exist?(pid_file)
|
243
|
+
STDERR.puts "!!! PID file #{pid_file} already exists." +
|
244
|
+
" Mongrel could be running already. " +
|
245
|
+
"Check your #{settings[:log_file]} for errors."
|
246
|
+
STDERR.puts "!!! Exiting with error. " +
|
247
|
+
"You must stop mongrel and clear the .pid " +
|
248
|
+
"before I'll attempt a start."
|
249
|
+
exit 1
|
250
|
+
end
|
251
|
+
|
252
|
+
File.open(pid_file, 'w') { |fp| fp.puts $$.to_s }
|
253
|
+
end
|
254
|
+
|
255
|
+
def reopen_child_log(settings)
|
256
|
+
STDIN.reopen '/dev/null' rescue nil
|
257
|
+
STDOUT.reopen(settings[:log_file], 'a')
|
258
|
+
STDOUT.sync = true
|
259
|
+
STDERR.reopen(settings[:log_file], 'a')
|
260
|
+
STDERR.sync = true
|
261
|
+
end
|
262
|
+
|
263
|
+
def get_unbound_port
|
264
|
+
tmp_port = tmp_socket = nil
|
265
|
+
while ! tmp_socket do
|
266
|
+
begin
|
267
|
+
tmp_port = rand(32768) + 32768
|
268
|
+
tmp_socket = TCPServer.new('127.0.0.1', tmp_port)
|
269
|
+
rescue Errno::EADDRINUSE
|
270
|
+
STDERR.puts "Warning: failed temporarily bind to random port: " +
|
271
|
+
"#{tmp_port}, trying another..."
|
272
|
+
retry
|
273
|
+
end
|
274
|
+
end
|
275
|
+
tmp_socket.close
|
276
|
+
tmp_port
|
277
|
+
end
|
278
|
+
|
279
|
+
def rails_preload(settings)
|
280
|
+
GC.start
|
281
|
+
|
282
|
+
# start a random, temporary alternate port
|
283
|
+
settings = settings.dup
|
284
|
+
settings[:host] = '127.0.0.1'
|
285
|
+
settings[:port] = get_unbound_port
|
286
|
+
config = Mongrel::Rails::RailsConfigurator.new(settings) do
|
287
|
+
mime = {}
|
288
|
+
if defaults[:mime_map]
|
289
|
+
log "Loading additional MIME types from #{defaults[:mime_map]}"
|
290
|
+
mime = load_mime_map(defaults[:mime_map], mime)
|
291
|
+
end
|
292
|
+
|
293
|
+
listener do
|
294
|
+
uri defaults[:prefix] || "/",
|
295
|
+
:handler => rails(:mime => mime, :prefix => defaults[:prefix])
|
296
|
+
if defaults[:config_script]
|
297
|
+
log "Loading #{defaults[:config_script]} external config script"
|
298
|
+
run_config(defaults[:config_script])
|
299
|
+
end
|
300
|
+
log "Loading any Rails specific GemPlugins"
|
301
|
+
load_plugins
|
302
|
+
log "Rails loaded."
|
303
|
+
end
|
304
|
+
end # RailsConfigurator.new
|
305
|
+
config
|
306
|
+
end
|
307
|
+
|
308
|
+
def close_rails_connections
|
309
|
+
ActiveRecord::Base.connection.disconnect!
|
310
|
+
CACHE.reset if defined?(CACHE) && CACHE.respond_to?(:reset)
|
311
|
+
end
|
312
|
+
|
313
|
+
def open_rails_connections
|
314
|
+
ActiveRecord::Base.establish_connection
|
315
|
+
end
|
316
|
+
|
317
|
+
def wait_for_child_ready(address, port)
|
318
|
+
sleep 1
|
319
|
+
tries = 10
|
320
|
+
begin
|
321
|
+
http = Net::HTTP.new(address, port)
|
322
|
+
http.start
|
323
|
+
ret = http.get('/')
|
324
|
+
STDERR.puts "http://#{address}:#{port} ready: #{ret}"
|
325
|
+
rescue Errno::ECONNREFUSED => err
|
326
|
+
if (tries -= 1) > 0
|
327
|
+
STDERR.puts "Waiting for http://#{address}:#{port} to respond, " +
|
328
|
+
"#{tries} tries left"
|
329
|
+
sleep 1
|
330
|
+
retry
|
331
|
+
else
|
332
|
+
throw err
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
def spawn_child(settings, port)
|
338
|
+
fork {
|
339
|
+
GC.start
|
340
|
+
settings[:port] = port
|
341
|
+
settings[:pid_file] = port_pid_file(port)
|
342
|
+
settings[:log_file] = port_log_file(port)
|
343
|
+
write_child_pid(settings)
|
344
|
+
reopen_child_log(settings)
|
345
|
+
|
346
|
+
$0 << " -p #{port}"
|
347
|
+
open_rails_connections
|
348
|
+
config = Mongrel::Rails::RailsConfigurator.new(settings) do
|
349
|
+
if defaults[:debug]
|
350
|
+
log "Installing debugging prefixed filters. " +
|
351
|
+
"Look in log/mongrel_debug for the files."
|
352
|
+
debug "/"
|
353
|
+
end
|
354
|
+
|
355
|
+
listener do
|
356
|
+
uri defaults[:prefix] || "/", :handler => rails
|
357
|
+
if defaults[:config_script]
|
358
|
+
log "Loading #{defaults[:config_script]} external config script"
|
359
|
+
run_config(defaults[:config_script])
|
360
|
+
end
|
361
|
+
setup_rails_signals
|
362
|
+
end # listener
|
363
|
+
|
364
|
+
end # config
|
365
|
+
config.log "Mongrel available at #{settings[:host]}:#{port}"
|
366
|
+
config.run
|
367
|
+
GC.start
|
368
|
+
config.join
|
369
|
+
exit 0
|
370
|
+
} # fork
|
371
|
+
wait_for_child_ready(settings[:host], port)
|
372
|
+
rescue Object => err
|
373
|
+
STDERR.puts "Failed to spawn child on port #{port}: #{err.inspect}"
|
374
|
+
end # spawn_child
|
375
|
+
end
|
376
|
+
|
377
|
+
|
378
|
+
class Start < GemPlugin::Plugin "/commands"
|
379
|
+
include Mongrel::Command::Base
|
380
|
+
include Base
|
381
|
+
|
382
|
+
def configure
|
383
|
+
options start_options
|
384
|
+
end
|
385
|
+
|
386
|
+
def validate
|
387
|
+
validate_start_options
|
388
|
+
end
|
389
|
+
|
390
|
+
def run
|
391
|
+
restart_run(false)
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
|
396
|
+
class Stop < GemPlugin::Plugin "/commands"
|
397
|
+
include Mongrel::Command::Base
|
398
|
+
include Base
|
399
|
+
|
400
|
+
def configure
|
401
|
+
options stop_options
|
402
|
+
end
|
403
|
+
|
404
|
+
def validate
|
405
|
+
validate_stop_options
|
406
|
+
end
|
407
|
+
|
408
|
+
def run
|
409
|
+
settings = { :cwd => @cwd, :only => @only, :wait => @wait,
|
410
|
+
:port => @port, :servers => @servers,
|
411
|
+
:pid_file => @pid_file }
|
412
|
+
read_options(settings)
|
413
|
+
|
414
|
+
each_port(settings) { |port| stop_child(settings, port) }
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
|
419
|
+
class Restart < GemPlugin::Plugin "/commands"
|
420
|
+
include Mongrel::Command::Base
|
421
|
+
include Base
|
422
|
+
|
423
|
+
def configure
|
424
|
+
options start_options(true)
|
425
|
+
end
|
426
|
+
|
427
|
+
def validate
|
428
|
+
validate_stop_options && validate_start_options
|
429
|
+
end
|
430
|
+
|
431
|
+
def run
|
432
|
+
restart_run(true)
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
end
|
437
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
# Gem::Specification for Mongrel_cow_cluster-0.3.5
|
3
|
+
# Originally generated by Echoe
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = %q{mongrel_cow_cluster}
|
7
|
+
s.version = "0.3.5"
|
8
|
+
|
9
|
+
s.specification_version = 2 if s.respond_to? :specification_version=
|
10
|
+
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.authors = ["Eric Wong"]
|
13
|
+
s.date = %q{2008-02-24}
|
14
|
+
s.description = %q{A Mongrel plugin that uses copy-on-write to lower resource impact and handle rolling restarts of several Mongrel processes.}
|
15
|
+
s.email = %q{}
|
16
|
+
s.files = ["CHANGELOG", "README", "Manifest", "LICENSE", "lib/mongrel_cow_cluster/init.rb", "resources/defaults.yaml", "tools/rakehelp.rb", "mongrel_cow_cluster.gemspec"]
|
17
|
+
s.has_rdoc = true
|
18
|
+
s.homepage = %q{http://mongrel-cow.rubyforge.org}
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
s.rubyforge_project = %q{mongrel-cow}
|
21
|
+
s.rubygems_version = %q{0.9.4.7}
|
22
|
+
s.summary = %q{A Mongrel plugin that uses copy-on-write to lower resource impact and handle rolling restarts of several Mongrel processes.}
|
23
|
+
|
24
|
+
s.add_dependency(%q<mongrel>, [">= 0", "= 1.0.1"])
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
# # Original Rakefile source (requires the Echoe gem):
|
29
|
+
#
|
30
|
+
# require 'echoe'
|
31
|
+
# echoe = Echoe.new('mongrel_cow_cluster') do |p|
|
32
|
+
# p.author = 'Eric Wong'
|
33
|
+
# p.summary = 'A Mongrel plugin that uses copy-on-write to lower ' \
|
34
|
+
# 'resource impact and handle rolling restarts of ' \
|
35
|
+
# 'several Mongrel processes.'
|
36
|
+
# p.url = 'http://mongrel-cow.rubyforge.org'
|
37
|
+
# p.rubyforge_name = 'mongrel-cow'
|
38
|
+
# p.dependencies = [ 'mongrel >= 1.0.1' ]
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# task :eric_release => [:clean, :package] do
|
42
|
+
# sh %{git log > pkg/#{echoe.name}-changelog}
|
43
|
+
# FileUtils.rmtree "pkg/#{echoe.name}-#{echoe.version}"
|
44
|
+
# sh %{git tag -s -u normalperson@yhbt.net v#{echoe.version}}
|
45
|
+
# sh %{git push bogomips}
|
46
|
+
# sh %{rsync -av pkg/ bogomips.org:/srv/bogomips/ruby/}
|
47
|
+
# end
|
data/tools/rakehelp.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
|
2
|
+
def make(makedir)
|
3
|
+
Dir.chdir(makedir) do
|
4
|
+
sh(PLATFORM =~ /win32/ ? 'nmake' : 'make')
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
def extconf(dir)
|
10
|
+
Dir.chdir(dir) do ruby "extconf.rb" end
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def setup_tests
|
15
|
+
Rake::TestTask.new do |t|
|
16
|
+
t.libs << "test"
|
17
|
+
t.test_files = FileList['test/test*.rb']
|
18
|
+
t.verbose = true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def setup_clean otherfiles
|
24
|
+
files = ['build/*', '**/*.o', '**/*.so', '**/*.a', 'lib/*-*', '**/*.log'] + otherfiles
|
25
|
+
CLEAN.include(files)
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def setup_rdoc files
|
30
|
+
Rake::RDocTask.new do |rdoc|
|
31
|
+
rdoc.rdoc_dir = 'doc/rdoc'
|
32
|
+
rdoc.options << '--line-numbers'
|
33
|
+
rdoc.rdoc_files.add(files)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def setup_extension(dir, extension)
|
39
|
+
ext = "ext/#{dir}"
|
40
|
+
ext_so = "#{ext}/#{extension}.#{Config::CONFIG['DLEXT']}"
|
41
|
+
ext_files = FileList[
|
42
|
+
"#{ext}/*.c",
|
43
|
+
"#{ext}/*.h",
|
44
|
+
"#{ext}/extconf.rb",
|
45
|
+
"#{ext}/Makefile",
|
46
|
+
"lib"
|
47
|
+
]
|
48
|
+
|
49
|
+
task "lib" do
|
50
|
+
directory "lib"
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "Builds just the #{extension} extension"
|
54
|
+
task extension.to_sym => ["#{ext}/Makefile", ext_so ]
|
55
|
+
|
56
|
+
file "#{ext}/Makefile" => ["#{ext}/extconf.rb"] do
|
57
|
+
extconf "#{ext}"
|
58
|
+
end
|
59
|
+
|
60
|
+
file ext_so => ext_files do
|
61
|
+
make "#{ext}"
|
62
|
+
cp ext_so, "lib"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def base_gem_spec(pkg_name, pkg_version)
|
68
|
+
pkg_version = pkg_version
|
69
|
+
pkg_name = pkg_name
|
70
|
+
pkg_file_name = "#{pkg_name}-#{pkg_version}"
|
71
|
+
Gem::Specification.new do |s|
|
72
|
+
s.name = pkg_name
|
73
|
+
s.version = pkg_version
|
74
|
+
s.platform = Gem::Platform::RUBY
|
75
|
+
s.has_rdoc = true
|
76
|
+
s.extra_rdoc_files = [ "README.txt" ]
|
77
|
+
|
78
|
+
s.files = %w(COPYING README.txt Rakefile) +
|
79
|
+
Dir.glob("{bin,doc/rdoc,test,lib}/**/*") +
|
80
|
+
Dir.glob("ext/**/*.{h,c,rb}") +
|
81
|
+
Dir.glob("examples/**/*.rb") +
|
82
|
+
Dir.glob("tools/*.rb")
|
83
|
+
|
84
|
+
s.require_path = "lib"
|
85
|
+
s.extensions = FileList["ext/**/extconf.rb"].to_a
|
86
|
+
s.bindir = "bin"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def setup_gem(pkg_name, pkg_version)
|
91
|
+
spec = base_gem_spec(pkg_name, pkg_version)
|
92
|
+
yield spec if block_given?
|
93
|
+
|
94
|
+
Rake::GemPackageTask.new(spec) do |p|
|
95
|
+
p.gem_spec = spec
|
96
|
+
# win32 chokes on this
|
97
|
+
p.need_tar = true if RUBY_PLATFORM !~ /mswin/
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def setup_win32_gem(pkg_name, pkg_version)
|
102
|
+
spec = base_gem_spec(pkg_name, pkg_version)
|
103
|
+
yield spec if block_given?
|
104
|
+
|
105
|
+
Gem::Builder.new(spec).build
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
|
110
|
+
|
111
|
+
|
112
|
+
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
|
117
|
+
|
118
|
+
|
119
|
+
|
120
|
+
|
121
|
+
|
122
|
+
|
123
|
+
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.4.7
|
3
|
+
specification_version: 2
|
4
|
+
name: mongrel_cow_cluster
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.3.5
|
7
|
+
date: 2008-02-24 00:00:00 -08:00
|
8
|
+
summary: A Mongrel plugin that uses copy-on-write to lower resource impact and handle rolling restarts of several Mongrel processes.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: ""
|
12
|
+
homepage: http://mongrel-cow.rubyforge.org
|
13
|
+
rubyforge_project: mongrel-cow
|
14
|
+
description: A Mongrel plugin that uses copy-on-write to lower resource impact and handle rolling restarts of several Mongrel processes.
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: "0"
|
30
|
+
version:
|
31
|
+
platform: ruby
|
32
|
+
signing_key:
|
33
|
+
cert_chain: []
|
34
|
+
|
35
|
+
post_install_message:
|
36
|
+
authors:
|
37
|
+
- Eric Wong
|
38
|
+
files:
|
39
|
+
- CHANGELOG
|
40
|
+
- README
|
41
|
+
- Manifest
|
42
|
+
- LICENSE
|
43
|
+
- lib/mongrel_cow_cluster/init.rb
|
44
|
+
- resources/defaults.yaml
|
45
|
+
- tools/rakehelp.rb
|
46
|
+
- mongrel_cow_cluster.gemspec
|
47
|
+
test_files: []
|
48
|
+
|
49
|
+
rdoc_options: []
|
50
|
+
|
51
|
+
extra_rdoc_files: []
|
52
|
+
|
53
|
+
executables: []
|
54
|
+
|
55
|
+
extensions: []
|
56
|
+
|
57
|
+
requirements: []
|
58
|
+
|
59
|
+
dependencies:
|
60
|
+
- !ruby/object:Gem::Dependency
|
61
|
+
name: mongrel
|
62
|
+
version_requirement:
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
- - "="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: 1.0.1
|
71
|
+
version:
|