mongrel_cow_cluster 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|