Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Always restart #110

Closed
wants to merge 8 commits into from

3 participants

@aaronvb

Always restart if always_restart.txt is present.

aaronvb added some commits
@aaronvb aaronvb Added queryAlwaysRestartFile function which checks if always_restart.…
…txt exists in the tmp folder. Added alwaysrestart flag variable to prevent restart loop. Modified restartIfNecessary function to check existence of always_restart.txt if mtime has not changed on restart.txt assuming we're not touching restart.txt
19fc8b5
@aaronvb aaronvb Added tests for always_restart.txt 874026c
@sstephenson
Owner

I'm afraid I won't be able to use your implementation for the reasons outlined in josh/nack#8. But you've written some excellent test cases that should work regardless of the underlying implementation.

Thanks very much! I'll merge this (with a new implementation) once it's in nack.

@aaronvb

I definitely see the problems with multiple requests and timeouts, I should have written tests for that. My main idea was to try hooking into the already available restart function at the http request level.

Placing the logic at the nack level seems more appropriate, and by having each process terminate after every request. Thanks for looking though!

@sstephenson
Owner

Making progress: https://github.com/37signals/pow/compare/always-restart

Just down to one failing test case.

aaronvb added some commits
@aaronvb aaronvb js files 8bd628b
@aaronvb aaronvb Merge remote branch 'upstream/master' into always_restart
Conflicts:
	lib/rack_application.js
	src/rack_application.coffee
e0fe025
@aaronvb aaronvb Merge remote branch 'upstream/always-restart' into always_restart
Conflicts:
	lib/rack_application.js
	src/rack_application.coffee
4deda27
@josh
Owner

Merged 954efb1

@josh josh closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 17, 2011
  1. @aaronvb

    Added queryAlwaysRestartFile function which checks if always_restart.…

    aaronvb authored
    …txt exists in the tmp folder. Added alwaysrestart flag variable to prevent restart loop. Modified restartIfNecessary function to check existence of always_restart.txt if mtime has not changed on restart.txt assuming we're not touching restart.txt
  2. @aaronvb
Commits on Apr 18, 2011
  1. @sstephenson
  2. @sstephenson

    Merge the test cases from 'aaronvb/always_restart'

    sstephenson authored
    Conflicts:
    	src/rack_application.coffee
  3. @sstephenson
  4. @aaronvb

    js files

    aaronvb authored
  5. @aaronvb

    Merge remote branch 'upstream/master' into always_restart

    aaronvb authored
    Conflicts:
    	lib/rack_application.js
    	src/rack_application.coffee
  6. @aaronvb

    Merge remote branch 'upstream/always-restart' into always_restart

    aaronvb authored
    Conflicts:
    	lib/rack_application.js
    	src/rack_application.coffee
This page is out of date. Refresh to see the latest.
View
2  lib/configuration.js
@@ -135,7 +135,7 @@
parts = host.slice(0, -domain.length - 1).split(".");
length = parts.length;
_results = [];
- for (i = 0; (0 <= length ? i < length : i > length); (0 <= length ? i += 1 : i -= 1)) {
+ for (i = 0; 0 <= length ? i < length : i > length; 0 <= length ? i++ : i--) {
_results.push(parts.slice(i, length).join("."));
}
return _results;
View
60 lib/rack_application.js
@@ -13,6 +13,7 @@
this.logger = this.configuration.getLogger(join("apps", basename(this.root)));
this.readyCallbacks = [];
this.quitCallbacks = [];
+ this.statCallbacks = [];
}
RackApplication.prototype.ready = function(callback) {
if (this.state === "ready") {
@@ -45,6 +46,21 @@
}
}, this));
};
+ RackApplication.prototype.setPoolRunOnceFlag = function(callback) {
+ if (!this.statCallbacks.length) {
+ exists(join(this.root, "tmp/always_restart.txt"), __bind(function(alwaysRestart) {
+ var statCallback, _i, _len, _ref;
+ this.pool.runOnce = alwaysRestart;
+ _ref = this.statCallbacks;
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ statCallback = _ref[_i];
+ statCallback();
+ }
+ return this.statCallbacks = [];
+ }, this));
+ }
+ return this.statCallbacks.push(callback);
+ };
RackApplication.prototype.loadScriptEnvironment = function(env, callback) {
return async.reduce([".powrc", ".envrc", ".powenv"], env, __bind(function(env, filename, callback) {
var script;
@@ -168,26 +184,34 @@
if (err) {
return next(err);
}
- return this.restartIfNecessary(__bind(function() {
- req.proxyMetaVariables = {
- SERVER_PORT: this.configuration.dstPort.toString()
- };
- try {
- return this.pool.proxy(req, res, __bind(function(err) {
- if (err) {
- this.quit();
+ return this.setPoolRunOnceFlag(__bind(function() {
+ return this.restartIfNecessary(__bind(function() {
+ req.proxyMetaVariables = {
+ SERVER_PORT: this.configuration.dstPort.toString()
+ };
+ try {
+ return this.pool.proxy(req, res, __bind(function(err) {
+ if (err) {
+ this.quit();
+ }
+ return next(err);
+ }, this));
+ } finally {
+ resume();
+ if (typeof callback == "function") {
+ callback();
}
- return next(err);
- }, this));
- } finally {
- resume();
- if (typeof callback == "function") {
- callback();
}
+<<<<<<< HEAD
+ this.flagForAlwaysRestart = true;
}
+=======
+ }, this));
+>>>>>>> upstream/always-restart
}, this));
}, this));
};
+
RackApplication.prototype.restart = function(callback) {
return this.quit(__bind(function() {
return this.ready(callback);
@@ -198,7 +222,13 @@
if (mtimeChanged) {
return this.restart(callback);
} else {
- return callback();
+ return this.queryAlwaysRestartFile(__bind(function(fileExists) {
+ if (fileExists) {
+ return this.restart(callback);
+ } else {
+ return callback();
+ }
+ }, this));
}
}, this));
};
View
6 lib/util.js
@@ -150,7 +150,11 @@
callback = options;
options = {};
} else {
- options != null ? options : options = {};
+ if (options != null) {
+ options;
+ } else {
+ options = {};
+ };
}
command = "" + options.before + ";\nsource '" + script + "' > /dev/null;\n'" + process.execPath + "' -e 'JSON.stringify(process.env)'";
return exec(command, {
View
2  package.json
@@ -13,7 +13,7 @@
, "coffee-script": ">= 1.0.0"
, "connect": ">= 1.0.3"
, "log": ">= 1.1.1"
- , "nack": ">= 0.10.3"
+ , "nack": "https://github.com/josh/nack/tarball/4857d587bf71a6e22b8e1fdac9f9dae707785d78"
, "ndns": ">= 0.1.2"
}
, "bundleDependencies" : [ "ndns" ]
View
37 src/rack_application.coffee
@@ -38,7 +38,8 @@ module.exports = class RackApplication
constructor: (@configuration, @root) ->
@logger = @configuration.getLogger join "apps", basename @root
@readyCallbacks = []
- @quitCallbacks = []
+ @quitCallbacks = []
+ @statCallbacks = []
# Queue `callback` to be invoked when the application becomes ready,
# then start the initialization process. If the application's state
@@ -74,6 +75,19 @@ module.exports = class RackApplication
@mtime = stats.mtime.getTime()
callback lastMtime isnt @mtime
+ # Check to see if `tmp/always_restart.txt` is present in the
+ # application root, and set the pool's `runOnce` option
+ # accordingly. Invoke `callback` when the existence check has
+ # finished. (Multiple calls to this method are aggregated.)
+ setPoolRunOnceFlag: (callback) ->
+ unless @statCallbacks.length
+ exists join(@root, "tmp/always_restart.txt"), (alwaysRestart) =>
+ @pool.runOnce = alwaysRestart
+ statCallback() for statCallback in @statCallbacks
+ @statCallbacks = []
+
+ @statCallbacks.push callback
+
# Collect environment variables from `.powrc` and `.powenv`, in that
# order, if present. The idea is that `.powrc` files can be checked
# into a source code repository for global configuration, leaving
@@ -190,16 +204,17 @@ module.exports = class RackApplication
resume = pause req
@ready (err) =>
return next err if err
- @restartIfNecessary =>
- req.proxyMetaVariables =
- SERVER_PORT: @configuration.dstPort.toString()
- try
- @pool.proxy req, res, (err) =>
- @quit() if err
- next err
- finally
- resume()
- callback?()
+ @setPoolRunOnceFlag =>
+ @restartIfNecessary =>
+ req.proxyMetaVariables =
+ SERVER_PORT: @configuration.dstPort.toString()
+ try
+ @pool.proxy req, res, (err) =>
+ @quit() if err
+ next err
+ finally
+ resume()
+ callback?()
# Terminate the application, re-initialize it, and invoke the given
# callback when the application's state becomes ready.
View
62 test/test_rack_application.coffee
@@ -71,6 +71,68 @@ module.exports = testCase
test.same pid, newpid
done -> fs.unlink restart, -> test.done()
+ "handling a request, always_restart.txt present, request": (test) ->
+ test.expect 3
+ always_restart = fixturePath("apps/pid/tmp/always_restart.txt")
+ serveApp "apps/pid", (request, done) ->
+ fs.unlink always_restart, ->
+ request "GET", "/", (body) ->
+ test.ok pid = parseInt body
+ touch always_restart, ->
+ request "GET", "/", (body) ->
+ test.ok newpid = parseInt body
+ test.ok pid isnt newpid
+ done -> fs.unlink always_restart, -> test.done()
+
+ "always_restart.txt present, handling a request, request": (test) ->
+ test.expect 3
+ touch always_restart = fixturePath("apps/pid/tmp/always_restart.txt"), ->
+ serveApp "apps/pid", (request, done) ->
+ request "GET", "/", (body) ->
+ test.ok pid = parseInt body
+ request "GET", "/", (body) ->
+ test.ok newpid = parseInt body
+ test.ok pid isnt newpid
+ done -> fs.unlink always_restart, -> test.done()
+
+ "always_restart.txt present, handling a request, touch restart.txt, request": (test) ->
+ test.expect 3
+ touch always_restart = fixturePath("apps/pid/tmp/always_restart.txt"), ->
+ serveApp "apps/pid", (request, done) ->
+ request "GET", "/", (body) ->
+ test.ok pid = parseInt body
+ touch restart = fixturePath("apps/pid/tmp/restart.txt"), ->
+ request "GET", "/", (body) ->
+ test.ok newpid = parseInt body
+ test.ok pid isnt newpid
+ done -> fs.unlink always_restart, fs.unlink restart, -> test.done()
+
+ "handling the initial request when restart.txt and always_restart.txt is present": (test) ->
+ test.expect 3
+ touch always_restart = fixturePath("apps/pid/tmp/always_restart.txt"), ->
+ touch restart = fixturePath("apps/pid/tmp/restart.txt"), ->
+ serveApp "apps/pid", (request, done) ->
+ request "GET", "/", (body) ->
+ test.ok pid = parseInt body
+ request "GET", "/", (body) ->
+ test.ok newpid = parseInt body
+ test.ok pid isnt newpid
+ done -> fs.unlink restart, fs.unlink always_restart, -> test.done()
+
+ "always_restart.txt present, handling a request, request, request": (test) ->
+ test.expect 5
+ touch always_restart = fixturePath("apps/pid/tmp/always_restart.txt"), ->
+ serveApp "apps/pid", (request, done) ->
+ request "GET", "/", (body) ->
+ test.ok pid = parseInt body
+ request "GET", "/", (body) ->
+ test.ok newpid = parseInt body
+ test.ok pid isnt newpid
+ request "GET", "/", (body) ->
+ test.ok newerpid = parseInt body
+ test.ok newpid isnt newerpid
+ done -> fs.unlink always_restart, -> test.done()
+
"custom environment": (test) ->
test.expect 3
serveApp "apps/env", (request, done) ->
Something went wrong with that request. Please try again.