MDL-65201 Backup: Automated backup course ordering

This patch adds sort ordering to the way courses are
automatically backed up. It sets the following preference:

Backup courses that do not have an entry in backup_courses first,
as they are likely new and never been backed up.
Backup courses that have the oldest next start time in backup_courses.
Give preference to most recently modified courses over other courses.
This commit is contained in:
Matt Porritt 2019-03-28 04:24:04 +00:00
parent 29c395187f
commit c2716719a6
2 changed files with 145 additions and 1 deletions

View File

@ -68,6 +68,40 @@ abstract class backup_cron_automated_helper {
/** Automated backup storage in course backup filearea and specified directory */
const STORAGE_COURSE_AND_DIRECTORY = 2;
/**
* Get the courses to backup.
*
* When there are multiple courses to backup enforce some order to the record set.
* The following is the preference order.
* First backup courses that do not have an entry in backup_courses first,
* as they are likely new and never been backed up. Do the oldest modified courses first.
* Then backup courses that have previously been backed up starting with the oldest next start time.
*
* @param null|int $now timestamp to use in course selection.
* @return moodle_recordset The recordset of matching courses.
*/
protected static function get_courses($now = null) {
global $DB;
if ($now == null) {
$now = time();
}
$sql = 'SELECT c.*,
COALESCE(bc.nextstarttime, 1) nextstarttime
FROM {course} c
LEFT JOIN {backup_courses} bc ON bc.courseid = c.id
WHERE bc.nextstarttime IS NULL OR bc.nextstarttime < ?
ORDER BY nextstarttime ASC,
c.timemodified ASC';
$params = array(
$now, // Only get courses where the backup start time is in the past.
);
$rs = $DB->get_recordset_sql($sql, $params);
return $rs;
}
/**
* Runs the automated backups if required
*
@ -127,7 +161,7 @@ abstract class backup_cron_automated_helper {
$showtime = date('r', $nextstarttime);
}
$rs = $DB->get_recordset('course');
$rs = self::get_courses($now); // Get courses to backup.
foreach ($rs as $course) {
$backupcourse = $DB->get_record('backup_courses', array('courseid' => $course->id));
if (!$backupcourse) {

View File

@ -364,8 +364,107 @@ class backup_cron_helper_testcase extends advanced_testcase {
// Updated courses should be backed up.
$this->assertTrue(testable_backup_cron_automated_helper::testable_is_course_modified($course->id, $timepriortobackup));
}
/**
* Create courses and backup records for tests.
*
* @return array Created courses.
*/
private function course_setup() {
global $DB;
// Create test courses.
$course1 = $this->getDataGenerator()->create_course(array('timecreated' => 1551402000));
$course2 = $this->getDataGenerator()->create_course(array('timecreated' => 1552179600));
$course3 = $this->getDataGenerator()->create_course(array('timecreated' => 1552179600));
$course4 = $this->getDataGenerator()->create_course(array('timecreated' => 1552179600));
// Create backup course records for the courses that need them.
$backupcourse3 = new stdClass;
$backupcourse3->courseid = $course3->id;
$backupcourse3->laststatus = testable_backup_cron_automated_helper::BACKUP_STATUS_OK;
$backupcourse3->nextstarttime = 1554858160;
$DB->insert_record('backup_courses', $backupcourse3);
$backupcourse4 = new stdClass;
$backupcourse4->courseid = $course4->id;
$backupcourse4->laststatus = testable_backup_cron_automated_helper::BACKUP_STATUS_OK;
$backupcourse4->nextstarttime = 1554858160;
$DB->insert_record('backup_courses', $backupcourse4);
return array($course1, $course2, $course3, $course4);
}
/**
* Test the selection and ordering of courses to be backed up.
*/
public function test_get_courses() {
$this->resetAfterTest();
list($course1, $course2, $course3, $course4) = $this->course_setup();
$now = 1559215025;
// Get the courses in order.
$courseset = testable_backup_cron_automated_helper::testable_get_courses($now);
$coursearray = array();
foreach ($courseset as $course) {
if ($course->id != SITEID) { // Skip system course for test.
$coursearray[] = $course->id;
}
}
$courseset->close();
// First should be course 1, it is the oldest modified without a backup.
$this->assertEquals($course1->id, $coursearray[0]);
// Second should be course 2, it is the next oldest modified without a backup.
$this->assertEquals($course2->id, $coursearray[1]);
// Third should be course 3, it is the course with the oldest backup.
$this->assertEquals($course3->id, $coursearray[2]);
// Fourth should be course 4, it is the course with the newest backup.
$this->assertEquals($course4->id, $coursearray[3]);
}
/**
* Test the selection and ordering of courses to be backed up.
* Where it is not yet time to start backups for courses with existing backups.
*/
public function test_get_courses_starttime() {
$this->resetAfterTest();
list($course1, $course2, $course3, $course4) = $this->course_setup();
$now = 1554858000;
// Get the courses in order.
$courseset = testable_backup_cron_automated_helper::testable_get_courses($now);
$coursearray = array();
foreach ($courseset as $course) {
if ($course->id != SITEID) { // Skip system course for test.
$coursearray[] = $course->id;
}
}
$courseset->close();
// Should only be two courses.
// First should be course 1, it is the oldest modified without a backup.
$this->assertEquals($course1->id, $coursearray[0]);
// Second should be course 2, it is the next oldest modified without a backup.
$this->assertEquals($course2->id, $coursearray[1]);
}
}
/**
* Provides access to protected methods we want to explicitly test
*
@ -397,4 +496,15 @@ class testable_backup_cron_automated_helper extends backup_cron_automated_helper
public static function testable_is_course_modified($courseid, $since) {
return parent::is_course_modified($courseid, $since);
}
/**
* Provides access to protected method get_courses.
*
* @param int $now Timestamp to use.
* @return moodle_recordset The returned courses as a Moodle recordest.
*/
public static function testable_get_courses($now) {
return parent::get_courses($now);
}
}