From 5821a7c5f121bbc6b7dfd09640b917bd41e99217 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Tue, 30 Jul 2013 16:00:57 +1000 Subject: [PATCH] MDL-40893 backup: cache question answers for performance To constrain memory we only cache a single question's answers. A quiz must display this many answers, so it will not result in undue memory pressure. We now match on the full answer text rather than the first 255 characters. This removes the risk of matching questions with the same start. --- backup/moodle2/restore_qtype_plugin.class.php | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/backup/moodle2/restore_qtype_plugin.class.php b/backup/moodle2/restore_qtype_plugin.class.php index 99365bad2f2..60f599a38b9 100644 --- a/backup/moodle2/restore_qtype_plugin.class.php +++ b/backup/moodle2/restore_qtype_plugin.class.php @@ -35,6 +35,18 @@ defined('MOODLE_INTERNAL') || die(); */ abstract class restore_qtype_plugin extends restore_plugin { + /* + * A simple answer to id cache for a single questions answers. + * @var array + */ + private $questionanswercache = array(); + + /* + * The id of the current question in the questionanswercache. + * @var int + */ + private $questionanswercacheid = null; + /** * Add to $paths the restore_path_elements needed * to handle question_answers for a given question @@ -147,38 +159,32 @@ abstract class restore_qtype_plugin extends restore_plugin { // The question existed, we need to map the existing question_answers } else { - // Look in question_answers by answertext matching - $sql = 'SELECT id - FROM {question_answers} - WHERE question = ? - AND ' . $DB->sql_compare_text('answer', 255) . ' = ' . $DB->sql_compare_text('?', 255); - $params = array($newquestionid, $data->answertext); - $newitemid = $DB->get_field_sql($sql, $params); - - // Not able to find the answer, let's try cleaning the answertext - // of all the question answers in DB as slower fallback. MDL-30018. - if (!$newitemid) { + // Have we cached the current question? + if ($this->questionanswercacheid !== $newquestionid) { + // The question changed, purge and start again! + $this->questionanswercache = array(); $params = array('question' => $newquestionid); $answers = $DB->get_records('question_answers', $params, '', 'id, answer'); + $this->questionanswercacheid = $newquestionid; + // Cache all cleaned answers for a simple text match. foreach ($answers as $answer) { - // Clean in the same way than {@link xml_writer::xml_safe_utf8()}. + // MDL-30018: Clean in the same way as {@link xml_writer::xml_safe_utf8()}. $clean = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is','', $answer->answer); // Clean CTRL chars. $clean = preg_replace("/\r\n|\r/", "\n", $clean); // Normalize line ending. - if ($clean === $data->answertext) { - $newitemid = $data->id; - } + $this->questionanswercache[$clean] = $answer->id; } } - // If we haven't found the newitemid, something has gone really wrong, question in DB - // is missing answers, exception - if (!$newitemid) { + if (!isset($this->questionanswercache[$data->answertext])) { + // If we haven't found the matching answer, something has gone really wrong, the question in the DB + // is missing answers, throw an exception. $info = new stdClass(); $info->filequestionid = $oldquestionid; $info->dbquestionid = $newquestionid; $info->answer = $data->answertext; throw new restore_step_exception('error_question_answers_missing_in_db', $info); } + $newitemid = $this->questionanswercache[$data->answertext]; } // Create mapping (we'll use this intensively when restoring question_states. And also answerfeedback files) $this->set_mapping('question_answer', $oldid, $newitemid);