Error restoring backup containing random questions (3.3.4+ to 4.0.4+)

Error restoring backup containing random questions (3.3.4+ to 4.0.4+)

by Mark Brand -
Number of replies: 11

Hi everyone

I have a Moodle 3.3.4+ (Build: 20180118) course backup containing quizzes with random questions. I have been trying to restore that backup to a Moodle 4.0.4+ (Build: 20221007) installation. Debugging mode indicates the failure as follows:

Notice: Undefined property: stdClass::$includingsubcategories in /opt/moodle/mod/quiz/backup/moodle2/restore_quiz_stepslib.php on line 346

Notice: Trying to get property 'qtype' of non-object in /opt/moodle/mod/quiz/backup/moodle2/restore_quiz_stepslib.php on line 335

Notice: Trying to get property 'questionbankentryid' of non-object in /opt/moodle/mod/quiz/backup/moodle2/restore_quiz_stepslib.php on line 359

... ultimately ending with "Error writing to database". I am now quite sure that this flows from changes made to the random question type at some point (https://tracker.moodle.org/browse/MDL-61380, perhaps), and my falling foul of a line beyond which backward compatibility seems to have broken. I've been trawling the source code hoping to muster enough understanding to attempt to fix this, but I'm actually quite stumped. Can anybody throw me any sort of lifeline, please?:

  1. It's clear enough from the above where the newly upgraded question needs to be written, but can anybody direct me to where it is being parsed from the backup file to begin with? It seems that the random question is a special case, and I'd like to see where that is implemented.
  2. Perhaps there is a more appropriate migration path than directly from 3.3.4+ to 4.0.4+. I am not at liberty to upgrade the 3.3.4+ server, but I might be able to spin up another Moodle 3.x as an intermediate step, if that has any prospect of success. What version might that be?

I do understand that I could simply rebuild all 325 random questions on the new server if there really is no other way, but I'm especially loathe to be losing all of the usage statistics gathered over time. I'm really hoping there's some way to do this migration without losing those. Any and all help is appreciated, thank you.

Mark

Average of ratings: -
In reply to Mark Brand

Re: Error restoring backup containing random questions (3.3.4+ to 4.0.4+)

by Howard Miller -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers
There shouldn't be any backward compatibility line. If this doesn't work (and it reproducible) then it's a bug.

I would check two things... make sure that there are no optional question types involved (missing on your Moodle 4 site) and... see if you can reproduce this with the absolute minimum configuration (just one simple question).
In reply to Howard Miller

Re: Error restoring backup containing random questions (3.3.4+ to 4.0.4+)

by Mark Brand -
Thank you, thank you! Excellent advice, of course. And I'm greatly encouraged that backward compatibility is a reasonable expectation. I am now experimenting with minimal configurations, and seeing successful restores: although (under debug mode) all of them do momentarily flash the warning "Undefined property: stdClass::$includingsubcategories". I don't generally rely on that option, so I'm not too phased right now. But I'll keep at it until I can reproduce the error that halts the restore. Thank you again, Howard.
In reply to Mark Brand

Re: Error restoring backup containing random questions (3.3.4+ to 4.0.4+)

by Mark Brand -
In response to Howard's advice, I've whittled down what seems to be the minimum procedure to reproduce this error.
On Moodle 3.3.4+:
* Create a new course "test" (I used Topics format, 0 sections)
* Save and display, Proceed to course content
* Add a Quiz to any section (all default options), Save & display, Edit quiz...
* Add a random question / Random question from an existing category / Add random question (Category should be "Default for test")
* click on (See questions), Create a new question ...
* Create a new True/False question in category "Default for test", name "question", text "question", Save changes
* Return to course view, duplicate quiz
* Backup the full course (Jump to final step), Download the file from Course backup area
On Moodle 4.0.4+:
* Site Administration / Courses / Restore course
* Drop downloaded backup file into "Import a backup file", click Restore
* click Continue, click Continue again under "Restore as a new course" (select a category, if necessary)
* Review Restore settings, click Next, Review Course settings, click Next, click Perform restore
Result: restore ultimately fails with "Error writing to database".
From what I dare to think I understand... the problem arises due to the two quizzes both containing a random question pointing to the same question bank category. This is a scenario that I happen to employ frequently: I group questions by subject matter, and my quizzes often overlap by including random questions from earlier sections as revision, interspersed with newer work. It seems this strategy has now failed me. A further observation: if I try to work around this by importing such quizzes one at a time (together with their attempts), I end up with duplicate questions in those overlapping categories. Naturally, this subverts other desirable functionality, like drawing per-question statistics. I am inclined to believe that this is a bug. Any advice about where to try to find it would be most welcome. I haven't been able to discern where, exactly, these questions are being parsed from the backup file. Thanks in advance for any assistance.
Average of ratings: Useful (1)
In reply to Mark Brand

Re: Error restoring backup containing random questions (3.3.4+ to 4.0.4+)

by Ken Task -
Picture of Particularly helpful Moodlers

Qualifications for this response ... none ... am not a questions expert ... but I do tinker ... a lot! smile

In your 4.0.4+, there is a command line only script called fix_orphaned_question_categories.php.

execute like: php fix_orphaned_question_categories.php -h

For help.

There is also an addon (used to be core) that might help:

https://moodle.org/plugins/tool_health

Among the test and thus recommended sql queries to fix are:

  • Random questions data consistency
  • Multi-answer questions data consistency
  • Question categories tree structure
As far as: "I haven't been able to discern where, exactly, these questions are being parsed from the backup file" does that mean in code?
The 3.3 backup contains a questions.xml file ... which is used to restore questions.
If you un-compress the backup file to a test directory, one can open the questions.xml file with a text editor to inspect all the info on that quiz.

Also, that 3.3 test backup has to be small ... not that I could replicate on my sandbox 4.0.4+, but could get a look at it if I had that backup.    Attach it here in this thread?

'SoS', Ken

Average of ratings: Useful (1)
In reply to Ken Task

Re: Error restoring backup containing random questions (3.3.4+ to 4.0.4+)

by Mark Brand -
Thank you Ken. I'm right there with you on the tinkering!
I'm aware of the script, though the duplicates that I get are not orphans - they're attached to different quizzes (though they're initially the same questions on my 3.3, just pointed to by different random questions). I will investigate that though, and thank you ever so much for all the other avenues to explore.
Yes, I was hoping for a pointer to somewhere in the code. I have been trawling through the questions.xml in my 3.3 backup. Now I'd like to know exactly how that is being parsed. Thank you also for suggesting that I include the backup in case anybody cares to have a go.
Average of ratings: Useful (1)
In reply to Mark Brand

Re: Error restoring backup containing random questions (3.3.4+ to 4.0.4+)

by Ken Task -
Picture of Particularly helpful Moodlers

Will download and look. smile

One other thing ... that health check I mentioned ... it is in your 3.3 but there is no link to it in admin menus.

So login as admin, and then via URL line go to:

/admin/tool/health/

Let's see what it might suggest for the 3.3 site.

'SoS', Ken


In reply to Mark Brand

Re: Error restoring backup containing random questions (3.3.4+ to 4.0.4+)

by Ken Task -
Picture of Particularly helpful Moodlers

The 3.3 doesn't have a column in mdl_question_references table called questionbankentryid.

The 4 expects it.

Debug info: Column 'questionbankentryid' cannot be null
INSERT INTO mdl_question_references (usingcontextid,component,questionarea,itemid,questionbankentryid,version)

VALUES(?,?,?,?,?,?)
[array (
0 => 532,
1 => 'mod_quiz',
2 => 'slot',
3 => 2,
4 => NULL,
5 => NULL

4 is NULL

But according to the explain on the 4 DB mysql> explain mdl_question_references;

| Field               | Type         | Null | Key | Default | Extra          |

| questionbankentryid | bigint(10)   | NO   | MUL | 0       |                |

Cannot be NULL

Now the question is to which xml file does one add a tag for such and what value should it have.

Do the same thing you did for the test 3.3 backup in the 4.

Backup.

Extract the backup and find the questionbankentryid tag in the xml's for the quizzes.

What are the values?

Pain in the arse, but one might have to edit the xml's to get what 4 is expecting.

Re-archive the backup so that moodle_backup.xml is found at the root of the archive when 4 unarchives it, then try it again! :|

Never easy! sad

'SoS', Ken


In reply to Ken Task

Re: Error restoring backup containing random questions (3.3.4+ to 4.0.4+)

by Mark Brand -
Always enlightening to see someone else's approach to debugging, thank you Ken. I concur: the null questionbankentryid halts the restore. I've traced that back into mod/quiz/backup/moodle2/restore_quiz_stepslib.php : process_quiz_question_legacy_instance($data), a method seemingly tasked (at least in part) with the ideosyncrasies of older random questions. However, that method starts out by making a query that produces an empty record (actually, it returns 'false'), which it fails to check, and that is what causes the sequence of warnings and errors which follow: first the check for random type (which can't succeed since the object is 'false'), then it wanders down the wrong branch of the if statement (which produces the null value for questionbankentryid), ultimately halting as you describe above. I must immediately admit, I haven't done this particular trace on the minimal test scenario I've sketched out above, though it certainly appears to do exactly this. Certainly, all my earlier testing (on my actual backups) appeared to trigger this series of events at every encounter with a random question. I now suspect it only happens once a random question is encountered which references the same question category as another, previously encountered random question (essentially, a duplicate random question). As I've indicated, I see two different behaviours depending on whether these 'duplicates' are encountered within the same backup file (the restore fails) or as the result of a merge of successive restores (produces duplicate questions in the question bank). From what I've been able to glean from recent dev discussions, there has been some sort of restructuring around random questions (actually, there's mention of doing away with qtype_random, which I'm not really sure what to make of...). I'm inclined to think that I'm caught up somewhere in the middle of whatever that was/is, an edge-case that wan't anticipated, perhaps.
I'm going to hold off on your suggestion of modifying the backup for now, but I might revisit that. I think the attempt to write to mdl_question_references is an error anyway. It appears to me that a random question should be producing an entry in a table called mdl_question_set_references, or something like that. I have a lot of head-scratching ahead, I imagine. Thanks again for your kind advice, Ken.
Average of ratings: Useful (1)
In reply to Mark Brand

Re: Error restoring backup containing random questions (3.3.4+ to 4.0.4+)

by Mark Brand -

This in response to my musings above, in case somebody stumbles upon this post.

I'm now 100% certain that my problem  arose as a result of incorrect handling of old 3.3 random questions, though I'm unsure about where to best correct this. The quick-n-dirty turned out to be this: after correctly importing a particular random question and "converting" it to the new format (involving "question_set_references", it would seem), the old question is being deleted. When another random question appears later in the backup, referencing the same category, it is no longer able to find the target question. Thus the query returns false, and without checks, everything goes downhill from there. Commenting out line 347 of mod/quiz/backup/moodle2/restore_quiz_stepslib.php gives me full and glorious restore satisfaction.

// question_delete_question($question->questionid);

I'm left wondering what the effect of those undeleted questions is, though. Perhaps that's where the fix_orphaned_question_categories script can help me out (thanks Ken!). I guess I'll find out, in due course.

Average of ratings: Useful (1)
In reply to Mark Brand

Re: Error restoring backup containing random questions (3.3.4+ to 4.0.4+)

by Ken Task -
Picture of Particularly helpful Moodlers

Ok, dumb question ... is/there are/there any reason(s) you couldn't upgrade your 3.3 instance to a version 4 of Moodle?  Can't do that as a hyperjump ... would have to 'march' it ... like 3.3 -> 3.5 -> 3.9 -> 4.0.4+

In the long run that might be the path of less pain!!!

'SoS', Ken

In reply to Ken Task

Re: Error restoring backup containing random questions (3.3.4+ to 4.0.4+)

by Mark Brand -
Not a dumb question at all. I've transfered from one department to another (some time ago), so while I'm able to access these older teaching materials of mine, I no longer have exclusive use of that server. But you have given me pause to consider whether shouldn't maybe spin up my own 3.3, do a full restore onto that, and then march forward as you suggest. This is my plan B then, once I give up on squashing the bug. Thank you Ken!