Skip to content

Commit

Permalink
MDL-40545 add $CFG->localcachedir intended for local caching on clust…
Browse files Browse the repository at this point in the history
…er nodes
  • Loading branch information
skodak committed Jul 20, 2013
1 parent 37824e7 commit 85b3806
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 12 deletions.
5 changes: 3 additions & 2 deletions admin/cli/install.php
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,9 @@
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') {
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 @@ -151,6 +151,7 @@

// 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.
Expand Down
7 changes: 5 additions & 2 deletions config-dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@
// $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
// );
Expand Down Expand Up @@ -353,10 +354,12 @@
//
// 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.
Expand Down
1 change: 1 addition & 0 deletions install.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@
$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';
Expand Down
6 changes: 6 additions & 0 deletions lib/moodlelib.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
8 changes: 7 additions & 1 deletion lib/setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -156,6 +157,11 @@
$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()
Expand Down
61 changes: 59 additions & 2 deletions lib/setuplib.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -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.
*
Expand Down
1 change: 1 addition & 0 deletions lib/testing/classes/util.php
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,7 @@ public static function reset_dataroot() {
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.
Expand Down
80 changes: 78 additions & 2 deletions lib/tests/setuplib_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
* @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.
Expand Down Expand Up @@ -126,7 +126,7 @@ public function test_exception_info_removes_serverpaths() {
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 = '';
Expand All @@ -142,4 +142,80 @@ public function test_exception_info_removes_serverpaths() {
$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));
}
}
1 change: 1 addition & 0 deletions lib/upgrade.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 8 additions & 3 deletions lib/upgradelib.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 85b3806

Please sign in to comment.