Commit 85b38061 authored by Petr Škoda's avatar Petr Škoda
Browse files

MDL-40545 add $CFG->localcachedir intended for local caching on cluster nodes

parent 37824e73
......@@ -394,8 +394,9 @@ if ($interactive) {
cli_error(get_string('pathserrcreatedataroot', 'install', $a));
}
}
$CFG->tempdir = $CFG->dataroot.'/temp';
$CFG->cachedir = $CFG->dataroot.'/cache';
$CFG->tempdir = $CFG->dataroot.'/temp';
$CFG->cachedir = $CFG->dataroot.'/cache';
$CFG->localcachedir = $CFG->dataroot.'/localcache';
// download required lang packs
if ($CFG->lang !== 'en') {
......
......@@ -151,6 +151,7 @@ $CFG->jsrev = 1;
// Unset cache and temp directories to reset them again with the new $CFG->dataroot.
unset($CFG->cachedir);
unset($CFG->localcachedir);
unset($CFG->tempdir);
// Continues setup.
......
......@@ -213,6 +213,7 @@ $CFG->admin = 'admin';
// $CFG->xsendfilealiases = array(
// '/dataroot/' => $CFG->dataroot,
// '/cachedir/' => '/var/www/moodle/cache', // for custom $CFG->cachedir locations
// '/localcachedir/' => '/var/local/cache', // for custom $CFG->localcachedir locations
// '/tempdir/' => '/var/www/moodle/temp', // for custom $CFG->tempdir locations
// '/filedir' => '/var/www/moodle/filedir', // for custom $CFG->filedir locations
// );
......@@ -353,10 +354,12 @@ $CFG->admin = 'admin';
//
// It is possible to specify different cache and temp directories, use local fast filesystem
// for normal web servers. Server clusters MUST use shared filesystem for cachedir!
// Localcachedir is intended for server clusters, it does not have to be shared by cluster nodes.
// The directories must not be accessible via web.
//
// $CFG->tempdir = '/var/www/moodle/temp';
// $CFG->cachedir = '/var/www/moodle/cache';
// $CFG->tempdir = '/var/www/moodle/temp'; // Files used during one HTTP request only.
// $CFG->cachedir = '/var/www/moodle/cache'; // Directory MUST BE SHARED by all cluster nodes, locking required.
// $CFG->localcachedir = '/var/local/cache'; // Intended for local node caching.
//
// Some filesystems such as NFS may not support file locking operations.
// Locking resolves race conditions and is strongly recommended for production servers.
......
......@@ -170,6 +170,7 @@ $CFG->httpswwwroot = $CFG->wwwroot;
$CFG->dataroot = $config->dataroot;
$CFG->tempdir = $CFG->dataroot.'/temp';
$CFG->cachedir = $CFG->dataroot.'/cache';
$CFG->localcachedir = $CFG->dataroot.'/localcache';
$CFG->admin = $config->admin;
$CFG->docroot = 'http://docs.moodle.org';
$CFG->langotherroot = $CFG->dataroot.'/lang';
......
......@@ -1560,6 +1560,12 @@ function purge_all_caches() {
// hack: this script may get called after the purifier was initialised,
// but we do not want to verify repeatedly this exists in each call
make_cache_directory('htmlpurifier');
// This is the only place where we purge local caches, we are only adding files there.
// The $CFG->localcachedirpurged flag forces local directories to be purged on cluster nodes.
remove_dir($CFG->localcachedir, true);
set_config('localcachedirpurged', time());
make_localcache_directory('', true);
}
/**
......
......@@ -37,7 +37,8 @@
* - $CFG->dirroot - Path to moodle's library folder on server's filesystem.
* - $CFG->libdir - Path to moodle's library folder on server's filesystem.
* - $CFG->tempdir - Path to moodle's temp file directory on server's filesystem.
* - $CFG->cachedir - Path to moodle's cache directory on server's filesystem.
* - $CFG->cachedir - Path to moodle's cache directory on server's filesystem (shared by cluster nodes).
* - $CFG->localcachedir - Path to moodle's local cache directory (not shared by cluster nodes).
*
* @global object $CFG
* @name $CFG
......@@ -156,6 +157,11 @@ if (!isset($CFG->cachedir)) {
$CFG->cachedir = "$CFG->dataroot/cache";
}
// Allow overriding of localcachedir.
if (!isset($CFG->localcachedir)) {
$CFG->localcachedir = "$CFG->dataroot/localcache";
}
// The current directory in PHP version 4.3.0 and above isn't necessarily the
// directory of the script when run from the command line. The require_once()
// would fail, so we'll have to chdir()
......
......@@ -529,7 +529,7 @@ function get_exception_info($ex) {
// Remove some absolute paths from message and debugging info.
$searches = array();
$replaces = array();
$cfgnames = array('tempdir', 'cachedir', 'themedir',
$cfgnames = array('tempdir', 'cachedir', 'localcachedir', 'themedir',
'langmenucachefile', 'langcacheroot', 'dataroot', 'dirroot');
foreach ($cfgnames as $cfgname) {
if (property_exists($CFG, $cfgname)) {
......@@ -1311,7 +1311,10 @@ function make_upload_directory($directory, $exceptiononerror = true) {
debugging('Use make_temp_directory() for creation of temporary directory and $CFG->tempdir to get the location.');
} else if (strpos($directory, 'cache/') === 0 or $directory === 'cache') {
debugging('Use make_cache_directory() for creation of chache directory and $CFG->cachedir to get the location.');
debugging('Use make_cache_directory() for creation of cache directory and $CFG->cachedir to get the location.');
} else if (strpos($directory, 'localcache/') === 0 or $directory === 'localcache') {
debugging('Use make_localcache_directory() for creation of local cache directory and $CFG->localcachedir to get the location.');
}
protect_directory($CFG->dataroot);
......@@ -1340,6 +1343,8 @@ function make_temp_directory($directory, $exceptiononerror = true) {
/**
* Create a directory under cachedir and make sure it is writable.
*
* Note: this cache directory is shared by all cluster nodes.
*
* @param string $directory the full path of the directory to be created under $CFG->cachedir
* @param bool $exceptiononerror throw exception if error encountered
* @return string|false Returns full path to directory if successful, false if not; may throw exception
......@@ -1355,6 +1360,58 @@ function make_cache_directory($directory, $exceptiononerror = true) {
return make_writable_directory("$CFG->cachedir/$directory", $exceptiononerror);
}
/**
* Create a directory under localcachedir and make sure it is writable.
* The files in this directory MUST NOT change, use revisions or content hashes to
* work around this limitation - this means you can only add new files here.
*
* The content of this directory gets purged automatically on all cluster nodes
* after calling purge_all_caches() before new data is written to this directory.
*
* Note: this local cache directory does not need to be shared by cluster nodes.
*
* @param string $directory the relative path of the directory to be created under $CFG->localcachedir
* @param bool $exceptiononerror throw exception if error encountered
* @return string|false Returns full path to directory if successful, false if not; may throw exception
*/
function make_localcache_directory($directory, $exceptiononerror = true) {
global $CFG;
make_writable_directory($CFG->localcachedir, $exceptiononerror);
if ($CFG->localcachedir !== "$CFG->dataroot/localcache") {
protect_directory($CFG->localcachedir);
} else {
protect_directory($CFG->dataroot);
}
if (!isset($CFG->localcachedirpurged)) {
$CFG->localcachedirpurged = 0;
}
$timestampfile = "$CFG->localcachedir/.lastpurged";
if (!file_exists($timestampfile)) {
touch($timestampfile);
@chmod($timestampfile, $CFG->filepermissions);
} else if (filemtime($timestampfile) < $CFG->localcachedirpurged) {
// This means our local cached dir was not purged yet.
remove_dir($CFG->localcachedir, true);
if ($CFG->localcachedir !== "$CFG->dataroot/localcache") {
protect_directory($CFG->localcachedir);
}
touch($timestampfile);
@chmod($timestampfile, $CFG->filepermissions);
clearstatcache();
}
if ($directory === '') {
return $CFG->localcachedir;
}
return make_writable_directory("$CFG->localcachedir/$directory", $exceptiononerror);
}
/**
* Checks if current user is a web crawler.
*
......
......@@ -599,6 +599,7 @@ abstract class testing_util {
make_temp_directory('');
make_cache_directory('');
make_cache_directory('htmlpurifier');
make_localcache_directory('');
// Reset the cache API so that it recreates it's required directories as well.
cache_factory::reset();
// Purge all data from the caches. This is required for consistency.
......
......@@ -31,7 +31,7 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2012 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_setuplib_testcase extends basic_testcase {
class core_setuplib_testcase extends advanced_testcase {
/**
* Test get_docs_url_standard in the normal case when we should link to Moodle docs.
......@@ -126,7 +126,7 @@ class core_setuplib_testcase extends basic_testcase {
global $CFG;
// This doesn't test them all possible ones, but these are set for unit tests.
$cfgnames = array('dataroot', 'dirroot', 'tempdir', 'cachedir');
$cfgnames = array('dataroot', 'dirroot', 'tempdir', 'cachedir', 'localcachedir');
$fixture = '';
$expected = '';
......@@ -142,4 +142,80 @@ class core_setuplib_testcase extends basic_testcase {
$this->assertContains($expected, $exceptioninfo->message, 'Exception message does not contain system paths');
$this->assertContains($expected, $exceptioninfo->debuginfo, 'Exception debug info does not contain system paths');
}
public function test_localcachedir() {
global $CFG;
$this->resetAfterTest(true);
// Test default location - can not be modified in phpunit tests because we override everything in config.php.
$this->assertSame("$CFG->dataroot/localcache", $CFG->localcachedir);
$now = time();
$timestampfile = "$CFG->localcachedir/.lastpurged";
$dir = make_localcache_directory('', false);
$this->assertSame($CFG->localcachedir, $dir);
$this->assertFileNotExists("$CFG->localcachedir/.htaccess");
$this->assertFileExists($timestampfile);
$this->assertGreaterThanOrEqual($now, filemtime($timestampfile));
$this->assertLessThanOrEqual(time(), filemtime($timestampfile));
$dir = make_localcache_directory('test/test', false);
$this->assertSame("$CFG->localcachedir/test/test", $dir);
// Test custom location.
$CFG->localcachedir = "$CFG->dataroot/testlocalcache";
$now = time();
$timestampfile = "$CFG->localcachedir/.lastpurged";
$this->assertFileNotExists($timestampfile);
$dir = make_localcache_directory('', false);
$this->assertSame($CFG->localcachedir, $dir);
$this->assertFileExists("$CFG->localcachedir/.htaccess");
$this->assertFileExists($timestampfile);
$this->assertGreaterThanOrEqual($now, filemtime($timestampfile));
$this->assertLessThanOrEqual(time(), filemtime($timestampfile));
$dir = make_localcache_directory('test', false);
$this->assertSame("$CFG->localcachedir/test", $dir);
$prevtime = filemtime($timestampfile);
$dir = make_localcache_directory('pokus', false);
$this->assertSame("$CFG->localcachedir/pokus", $dir);
$this->assertSame($prevtime, filemtime($timestampfile));
// Test purging.
$testfile = "$CFG->localcachedir/test/test.txt";
$this->assertTrue(touch($testfile));
$now = time();
set_config('localcachedirpurged', $now - 2);
purge_all_caches();
$this->assertFileNotExists($testfile);
$this->assertFileNotExists(dirname($testfile));
$this->assertFileExists($timestampfile);
$this->assertGreaterThanOrEqual($now, filemtime($timestampfile));
$this->assertLessThanOrEqual(time(), filemtime($timestampfile));
$this->assertGreaterThanOrEqual($now, $CFG->localcachedirpurged);
$this->assertLessThanOrEqual(time(), $CFG->localcachedirpurged);
// Simulates purge_all_caches() on another server node.
make_localcache_directory('test', false);
$this->assertTrue(touch($testfile));
set_config('localcachedirpurged', $now - 1);
$this->assertTrue(touch($timestampfile, $now - 2));
clearstatcache();
$this->assertSame($now - 2, filemtime($timestampfile));
$now = time();
$dir = make_localcache_directory('', false);
$this->assertSame("$CFG->localcachedir", $dir);
$this->assertFileNotExists($testfile);
$this->assertFileNotExists(dirname($testfile));
$this->assertFileExists($timestampfile);
$this->assertGreaterThanOrEqual($now, filemtime($timestampfile));
$this->assertLessThanOrEqual(time(), filemtime($timestampfile));
}
}
......@@ -10,6 +10,7 @@ information provided here is intended especially for developers.
core_component::get_plugin_list() does not accept empty parameter any more.
* Use core_text::* instead of textlib:: and also core_collator::* instead of collatorlib::*.
* Use new function moodleform::mock_submit() to simulate form submission in unit tests (backported).
* New $CFG->localcachedir setting useful for cluster nodes. Admins have to update X-Sendfile aliases if used.
DEPRECATIONS:
Various previously deprecated functions have now been altered to throw DEBUG_DEVELOPER debugging notices
......
......@@ -1467,12 +1467,17 @@ function install_core($version, $verbose) {
global $CFG, $DB;
// We can not call purge_all_caches() yet, make sure the temp and cache dirs exist and are empty.
make_cache_directory('', true);
remove_dir($CFG->cachedir.'', true);
make_temp_directory('', true);
make_cache_directory('', true);
remove_dir($CFG->localcachedir.'', true);
make_localcache_directory('', true);
remove_dir($CFG->tempdir.'', true);
make_writable_directory($CFG->dataroot.'/muc', true);
make_temp_directory('', true);
remove_dir($CFG->dataroot.'/muc', true);
make_writable_directory($CFG->dataroot.'/muc', true);
try {
set_time_limit(600);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment