[MediaWiki-commits] [Gerrit] Periodically check for configuration file changes. - change (mediawiki...OfflineContentGenerator)
jenkins-bot has submitted this change and it was merged. Change subject: Periodically check for configuration file changes. .. Periodically check for configuration file changes. This allows a service to gracefully restart when its configuration changes. If configuration files are maintained (eg, with puppet) and this service is automatically restarted (eg, with upstart), this saves an explicit restart step when deploying configuration changes. When the `config.coordinator.checkTime` variable is 0 (which is the default), this automatic check is disabled and you will have to manually restart the service (by sending SIGINT, SIGTERM, or SIGHUP) after the configuration is changed. Also change SIGHUP handler to graceful shutdown, since Unix convention is to use SIGUP to signal configuration file changes. Change-Id: I586c2c39b8efcacbd50e884374aaeae2890b4bb9 --- M defaults.js M lib/cli.js M mw-ocg-service.js 3 files changed, 61 insertions(+), 16 deletions(-) Approvals: Cscott: Looks good to me, approved jenkins-bot: Verified diff --git a/defaults.js b/defaults.js index edfc8ed..a97637a 100644 --- a/defaults.js +++ b/defaults.js @@ -23,6 +23,12 @@ /** Public hostname of this instance for HTTP GET requests for locally stored content. */ hostname: null, + + /** How often (in seconds) to check for configuration changes. +* We gracefully shutdown when we get a config change; presumably +* upstart or some other service runner will restart us. Defaults +* to zero which means, "never check for configuration changes". */ + checkTime: 0, }, /** Configuration for the frontend HTTP server thread. You can choose to serve * content via a local socket, or an IP address. If both are null the server will diff --git a/lib/cli.js b/lib/cli.js index 7972cdb..9fb0c88 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -18,9 +18,15 @@ // Parse configuration files, with optional command-line override. var parseConfig = exports.parseConfig = function(commanderConfig) { + var deps = [], latest = 0, req = function(f) { + f = path.resolve(__dirname, f); + deps.push('+' + f); // Record filenames for all config files parsed. + latest = Math.max(latest, fs.statSync(f).ctime.getTime()); + return require(f); + }; /* === Configuration Options & File === */ - var config = require('../defaults.js'), configPath = '..'; + var config = req('../defaults.js'), configPath = '..'; // Local configuration overrides. while (config.config) { var configFile = relativeTo(configPath, config.config); @@ -28,9 +34,10 @@ try { fs.statSync(configFile); } catch (e) { + deps.push('-' + configFile); break; // File not present. } - config = require(configFile)(config) || config; + config = req(configFile)(config) || config; configPath = path.dirname(configFile); } @@ -41,7 +48,7 @@ // to the current working directory instead of relative to the path of this // file. commanderConfig = relativeTo(process.cwd(), commanderConfig); - config = require(commanderConfig)(config) || config; + config = req(commanderConfig)(config) || config; } } catch (err) { console.error('Could not open configuration file %s! %s', commanderConfig, err); @@ -54,9 +61,38 @@ "Failed jobs shouldn't be gc'd before the lockout time expires." ); + // Record all dependent files and the latest ctime. + config.config_deps = deps; + config.config_time = latest; return config; }; +// Check to see if any configuration files have changed. +var configChanged = exports.configChanged = function(config) { + var deps = config.config_deps, latest = config.config_time, i; + for (i = 0; i < deps.length; i++) { + var s; + try { + s = fs.statSync(deps[i].slice(1)); + } catch (e) { + s = null; + } + if (deps[i][0] === '+') { + if (s === null || s.ctime.getTime() > latest) { + // Newer file, or an expected file disappeared. + return true; + } + } else { + if (s !== null) { + // A file appeared! + return true; + } + } + } + // No changes.
[MediaWiki-commits] [Gerrit] Periodically check for configuration file changes. - change (mediawiki...OfflineContentGenerator)
Cscott has uploaded a new change for review. https://gerrit.wikimedia.org/r/284599 Change subject: Periodically check for configuration file changes. .. Periodically check for configuration file changes. This allows a service to gracefully restart when its configuration changes. Change-Id: I586c2c39b8efcacbd50e884374aaeae2890b4bb9 --- M defaults.js M lib/cli.js M mw-ocg-service.js 3 files changed, 61 insertions(+), 3 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Collection/OfflineContentGenerator refs/changes/99/284599/1 diff --git a/defaults.js b/defaults.js index edfc8ed..a97637a 100644 --- a/defaults.js +++ b/defaults.js @@ -23,6 +23,12 @@ /** Public hostname of this instance for HTTP GET requests for locally stored content. */ hostname: null, + + /** How often (in seconds) to check for configuration changes. +* We gracefully shutdown when we get a config change; presumably +* upstart or some other service runner will restart us. Defaults +* to zero which means, "never check for configuration changes". */ + checkTime: 0, }, /** Configuration for the frontend HTTP server thread. You can choose to serve * content via a local socket, or an IP address. If both are null the server will diff --git a/lib/cli.js b/lib/cli.js index 7972cdb..77d07a8 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -18,9 +18,15 @@ // Parse configuration files, with optional command-line override. var parseConfig = exports.parseConfig = function(commanderConfig) { + var deps = [], latest = 0, req = function(f) { + f = path.resolve(__dirname, f); + deps.push('+' + f); // record filenames for all config files parsed. + latest = Math.max(latest, fs.statSync(f).ctime.getTime()); + return require(f); + }; /* === Configuration Options & File === */ - var config = require('../defaults.js'), configPath = '..'; + var config = req('../defaults.js'), configPath = '..'; // Local configuration overrides. while (config.config) { var configFile = relativeTo(configPath, config.config); @@ -28,9 +34,10 @@ try { fs.statSync(configFile); } catch (e) { + deps.push('-' + configFile); break; // File not present. } - config = require(configFile)(config) || config; + config = req(configFile)(config) || config; configPath = path.dirname(configFile); } @@ -41,7 +48,7 @@ // to the current working directory instead of relative to the path of this // file. commanderConfig = relativeTo(process.cwd(), commanderConfig); - config = require(commanderConfig)(config) || config; + config = req(commanderConfig)(config) || config; } } catch (err) { console.error('Could not open configuration file %s! %s', commanderConfig, err); @@ -54,9 +61,38 @@ "Failed jobs shouldn't be gc'd before the lockout time expires." ); + // Record all dependent files and the latest ctime. + config.config_deps = deps; + config.config_time = latest; return config; }; +// Check to see if any configuration files have changed. +var configChanged = exports.configChanged = function(config) { + var deps = config.config_deps, latest = config.config_time, i; + for (i=0; ilatest) { + // Newer file, or an expected file disappeared. + return true; + } + } else { + if (s!==null) { + // A file appeared! + return true; + } + } + } + // No changes. + return false; +}; + // Set up logging. var setupLogging = exports.setupLogging = function(config, forceStdout) { var bunyan = require('bunyan'); diff --git a/mw-ocg-service.js b/mw-ocg-service.js index 9f99d66..e140ee6 100755 --- a/mw-ocg-service.js +++ b/mw-ocg-service.js @@ -71,8 +71,10 @@ var i; /* --- Thread management --- */ + var checkTimer = 0; var gracefulShutdown = function gracefulShutdown() { respawnWorkers