Skip to content

Commit

Permalink
MDL-40475 add alternative component cache location and other improvem…
Browse files Browse the repository at this point in the history
…ents

Improvements include:
* Alternative location might be useful when server administrator wants to maintain
  a local copy of component cache instead of using shared $CFG->cachedir.
* Component caching is now enabled in behat tests which should improve performance.
* Standardised ignoring of component caching.
* Fixed debug mode in ABORT_AFTER_CONFIG scripts.
* General documentation improvements.
  • Loading branch information
skodak committed Jul 12, 2013
1 parent 07bbbcf commit d7245e3
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 21 deletions.
111 changes: 111 additions & 0 deletions admin/cli/alternative_component_cache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* This hack is intended for clustered sites that do not want
* to use shared cachedir for component cache.
*
* This file needs to be called after any change in PHP files in dataroot,
* that is before upgrade and install.
*
* @package core
* @copyright 2013 Petr Skoda (skodak) {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/


define('CLI_SCRIPT', true);
define('ABORT_AFTER_CONFIG', true); // We need just the values from config.php.
define('CACHE_DISABLE_ALL', true); // This prevents reading of existing caches.
define('IGNORE_COMPONENT_CACHE', true);

require(__DIR__.'/../../config.php');
require_once($CFG->libdir.'/clilib.php');

// Now get cli options.
list($options, $unrecognized) = cli_get_params(
array(
'file' => false,
'rebuild' => false,
'print' => false,
'help' => false
),
array(
'h' => 'help'
)
);

if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized), 2);
}

if (!$options['rebuild'] and !$options['file'] and !$options['print']) {
$help =
"Create alternative component cache file
Options:
-h, --help Print out this help
--rebuild Rebuild \$CFG->alternative_component_cache file
--file=filepath Save component cache to file
--print Print component cache file content
Example:
\$ php admin/cli/rebuild_alternative_component_cache.php --rebuild
";

echo $help;
exit(0);
}

error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 1);

$content = core_component::get_cache_content();

if ($options['print']) {
echo $content;
exit(0);
}

if ($options['rebuild']) {
if (empty($CFG->alternative_component_cache)) {
fwrite(STDERR, 'config.php does not contain $CFG->alternative_component_cache setting');
fwrite(STDERR, "\n");
exit(2);
}
$target = $CFG->alternative_component_cache;
} else {
$target = $options['file'];
}

if (!$target) {
fwrite(STDERR, "Invalid target file $target");
fwrite(STDERR, "\n");
exit(1);
}

$bytes = file_put_contents($target, $content);

if (!$bytes) {
fwrite(STDERR, "Error writing to $target");
fwrite(STDERR, "\n");
exit(1);
}

// Success.
echo "File $target was updated\n";
exit(0);
2 changes: 2 additions & 0 deletions admin/cli/install.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@

define('PHPUNIT_TEST', false);

define('IGNORE_COMPONENT_CACHE', true);

// Check that PHP is of a sufficient version
if (version_compare(phpversion(), "5.3.3") < 0) {
$phpversion = phpversion();
Expand Down
1 change: 1 addition & 0 deletions admin/tool/behat/cli/init.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

// Is not really necessary but adding it as is a CLI_SCRIPT.
define('CLI_SCRIPT', true);
define('CACHE_DISABLE_ALL', true);

// Basic functions.
require_once(__DIR__ . '/../../../../lib/clilib.php');
Expand Down
1 change: 1 addition & 0 deletions admin/tool/behat/cli/util.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
define('CLI_SCRIPT', true);
define('ABORT_AFTER_CONFIG', true);
define('NO_OUTPUT_BUFFERING', true);
define('IGNORE_COMPONENT_CACHE', true);

error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', '1');
Expand Down
2 changes: 2 additions & 0 deletions admin/tool/phpunit/cli/init.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
opcache_reset();
}

define('IGNORE_COMPONENT_CACHE', true);

require_once(__DIR__.'/../../../../lib/clilib.php');
require_once(__DIR__.'/../../../../lib/phpunit/bootstraplib.php');
require_once(__DIR__.'/../../../../lib/testing/lib.php');
Expand Down
2 changes: 2 additions & 0 deletions admin/tool/phpunit/cli/util.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
die; // no access from web!
}

define('IGNORE_COMPONENT_CACHE', true);

require_once(__DIR__.'/../../../../lib/clilib.php');
require_once(__DIR__.'/../../../../lib/phpunit/bootstraplib.php');
require_once(__DIR__.'/../../../../lib/testing/lib.php');
Expand Down
1 change: 1 addition & 0 deletions install.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
define('AJAX_SCRIPT', false); // prevents some warnings later
define('CACHE_DISABLE_ALL', true); // Disables caching.. just in case.
define('PHPUNIT_TEST', false);
define('IGNORE_COMPONENT_CACHE', true);

// Servers should define a default timezone in php.ini, but if they don't then make sure something is defined.
// This is a quick hack. Ideally we should ask the admin for a value. See MDL-22625 for more on this.
Expand Down
78 changes: 57 additions & 21 deletions lib/classes/component.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,44 @@ protected static function init() {
return;
}

if (PHPUNIT_TEST or !empty($CFG->early_install_lang) or
(defined('BEHAT_UTIL') and BEHAT_UTIL) or
(defined('BEHAT_TEST') and BEHAT_TEST)) {
// 1/ Do not bother storing the file for unit tests,
// we need fresh copy for each execution and
// later we keep it in memory.
// 2/ We can not write to dataroot in installer yet.
if (defined('IGNORE_COMPONENT_CACHE') and IGNORE_COMPONENT_CACHE) {
self::fill_all_caches();
return;
}

// Note: cachedir MUST be shared by all servers in a cluster, sorry guys...
// MUC should use classloading, we can not depend on it here.
$cachefile = "$CFG->cachedir/core_component.php";
if (!empty($CFG->alternative_component_cache)) {
// Hack for heavily clustered sites that want to manage component cache invalidation manually.
$cachefile = $CFG->alternative_component_cache;

if (file_exists($cachefile)) {
if (CACHE_DISABLE_ALL) {
// Verify the cache state only on upgrade pages.
$content = self::get_cache_content();
if (sha1_file($cachefile) !== sha1($content)) {
die('Outdated component cache file defined in $CFG->alternative_component_cache, can not continue');
}
return;
}
$cache = array();
include($cachefile);
self::$plugintypes = $cache['plugintypes'];
self::$plugins = $cache['plugins'];
self::$subsystems = $cache['subsystems'];
self::$classmap = $cache['classmap'];
return;
}

if (!is_writable(dirname($cachefile))) {
die('Can not create alternative component cache file defined in $CFG->alternative_component_cache, can not continue');
}

// Lets try to create the file, it might be in some writable directory or a local cache dir.

} else {
// Note: $CFG->cachedir MUST be shared by all servers in a cluster,
// use $CFG->alternative_component_cache if you do not like it.
$cachefile = "$CFG->cachedir/core_component.php";
}

if (!CACHE_DISABLE_ALL and !self::is_developer()) {
// 1/ Use the cache only outside of install and upgrade.
Expand All @@ -105,7 +129,7 @@ protected static function init() {
} else if (!isset($cache['plugintypes']) or !isset($cache['plugins']) or !isset($cache['subsystems']) or !isset($cache['classmap'])) {
// Something is very wrong.
} else if ($cache['plugintypes']['mod'] !== "$CFG->dirroot/mod") {
// Dirroot was changed.
// $CFG->dirroot was changed.
} else {
// The cache looks ok, let's use it.
self::$plugintypes = $cache['plugintypes'];
Expand All @@ -114,27 +138,28 @@ protected static function init() {
self::$classmap = $cache['classmap'];
return;
}
// Note: we do not verify $CFG->admin here intentionally,
// they must visit admin/index.php after any change.
}
}

$cachedir = dirname($cachefile);
if (!is_dir($cachedir)) {
mkdir($cachedir, $CFG->directorypermissions, true);
}

if (!isset(self::$plugintypes)) {
self::fill_all_caches();

// This needs to be atomic and self-fixing as much as possible.

$content = self::get_cache_content();
if (file_exists($cachefile)) {
if (sha1_file($cachefile) === sha1($content)) {
return;
}
// Stale cache detected!
unlink($cachefile);
}

$cachedir = dirname($cachefile);
if (!is_dir($cachedir)) {
mkdir($cachedir, $CFG->directorypermissions, true);
}

if ($fp = @fopen($cachefile.'.tmp', 'xb')) {
fwrite($fp, $content);
fclose($fp);
Expand All @@ -157,11 +182,16 @@ protected static function init() {
protected static function is_developer() {
global $CFG;

if (!isset($CFG->config_php_settings['debug'])) {
if (isset($CFG->config_php_settings['debug'])) {
// Standard moodle script.
$debug = (int)$CFG->config_php_settings['debug'];
} else if (isset($CFG->debug)) {
// Usually script with ABORT_AFTER_CONFIG.
$debug = (int)$CFG->debug;
} else {
return false;
}

$debug = (int)$CFG->config_php_settings['debug'];
if ($debug & E_ALL and $debug & E_STRICT) {
return true;
}
Expand All @@ -172,9 +202,15 @@ protected static function is_developer() {
/**
* Create cache file content.
*
* @private this is intended for $CFG->alternative_component_cache only.
*
* @return string
*/
protected static function get_cache_content() {
public static function get_cache_content() {
if (!isset(self::$plugintypes)) {
self::fill_all_caches();
}

$cache = array(
'subsystems' => self::$subsystems,
'plugintypes' => self::$plugintypes,
Expand Down
4 changes: 4 additions & 0 deletions lib/phpunit/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
}
}

if (!defined('IGNORE_COMPONENT_CACHE')) {
define('IGNORE_COMPONENT_CACHE', true);
}

require_once(__DIR__.'/bootstraplib.php');
require_once(__DIR__.'/../testing/lib.php');
require_once(__DIR__.'/classes/autoloader.php');
Expand Down

0 comments on commit d7245e3

Please sign in to comment.