Showing posts sorted by date for query moodle app. Sort by relevance Show all posts
Showing posts sorted by date for query moodle app. Sort by relevance Show all posts

Sunday, April 06, 2025

long back-and-forth about Moodle quiz content not working

After an upgrade to Moodle 4.5.3, the admin of one of our Moodle instances complained about some content not working. It finally turned out to be due to the use of deprecated mod_questionnaire and probably some caching. Copy-pasting large parts of our conversation.

We are experiencing delays in server from yesterday and today.

It is unresponsive when we try to add contents to our upcoming module in <course> which may start tomorrow.

Logged in to the site OK. As I have mentioned before, please add content in small enough chunks to avoid timeouts.

The site was up after some time. But with no heavy activity still we find it not responsive. Particularly that involves heavy database calls like the quizzes. This happened yesterday and even today.

e.g., https://ourdomain.org/mod/quiz/view.php?id=5032 which has the quiz section, is not loading at all. 

Can you check if the database server is  healthy for <course>?

1. The link which you have sent does not load due to an invalid course module id - "Invalid course module ID: 5013 ... More information about this error"

2. Copy-pasting from an email I had sent to the team via SG on 11 Jan 2023 - 

Some suggestions to make the course(s) load faster:

1. Currently, if you click on one of the courses, a huge page, with many images, for all weeks, loads in the browser. We can make it load a bit faster using Cloudflare, but such a large page is bound to cause problems with older computers / machines with less RAM / on slower networks / etc. It would be better to show a collapsed view, with each week opening up only on clicking. (the collapse all link on the top right). Or on separate pages.

2. As I mentioned, please use jpg quality = 70 to 75 instead of png. 

3. I'll make the cloudflare changes to turn proxying on. 

4. Clicking on question bank also loads up 1000 questions at once. This would cause problems as the number of questions / size of each question increase. It would be desirable to load up fewer questions - maybe 100 at a time as with the link at the bottom, "Show 100 at a time"

<the link> This was working on Friday night. This is a valid link for the quiz section of module 8.
Discovered that quiz sections are not opening for previous modules also - which the participants successfully faced for the last 7 modules. All quizzes and question bank resources were working fine till last Friday. So there is something suspicious in the database server. 

We need all the previous module results and also the new quizzes that are to be opened tomorrow.

Can you please check the health of the app and database server?

Moodle is installed and the database is running, which is how we are able to log on, see course pages, etc. - so the "health of the app and database server" are fine. 

Digging deeper, in the plugins page, https://ourdomain.org/admin/plugins.php
I have seen some plugins "missing from disk" errors. These would have been "missing" since the Moodle upgrade of Mar 18.

Just to be safe, I have now copied these "missing from disk" plugins to the moodle plugins directory. It may be that these plugins are no longer supported, hence Moodle removed them - other plugins prompt for upgrades, which have been applied.

I have also turned off cloudflare caching, so that you will get more time before server timeout. But this may make pages slower to load for those taking the courses, so once you are done with admin tasks requiring longer db queries, please let me know, and I will again turn on cloudflare caching for faster response for the users of the course.

The link which you sent still gives the same error.

I'm not sure what else I can help you with. If you want raw database backups from the weekly backups, I can send you.

Do you have the list for missing plugins and updated ones? That can give some ideas. 

local/welcome
local/mailtest
report/customsql
blocks/accessibility
lib/editor/atto
mod/questionnaire 


Or can we revert to the last working version of Moodle? The troubleshoot time is less and we need to get the module ready for tomorrow morning at 4am.

Note: Also in general, the site is too slow when compared to a couple of weeks before.
As far as I know, Moodle is working fine. So, as far as I'm concerned, "last working version of Moodle" is the current live site.

I can put the site back to last week's or the week before's - but then, any responses from the people taking from the course or any other changes made on the site after last week or 2 weeks back would be wiped out. Do you really want to do that? I don't think that's a good idea.

Yes, the site would be slower now that I have removed cloudflare caching as mentioned in the previous email.

Thanks for the list. `mod/questionnaire` seems to be related. But we can wait and see if we can create another set of quiz sections.

On software upgrade -> yes if things dont work, we need to revert to the last working version. Not yet decided. Shall keep you updated as we are trying to create a new section.

(and 20 minutes later,)

Surprisingly things are working again.. Not sure how but it looks fine now. Also the site seems to be a bit faster than before. Not sure how?

No changes were done from our side.

Moodle does some scheduled tasks - which are listed under Site Administration > Server > Tasks > Scheduled tasks

My guess would be that after I restored the missing plugin files, some automated task ran at some point of time to either clear database cached replies, or to fix broken links or something like that. 

Anyway, all's well that ends well. But please don't put too much content on a single page - that would cause the site to load slowly. Please see Course Format > Course Layout > Show all sections on one page and change it to "Show one section per page" on each of the courses you're working on. That will make pages load much faster.

5 days later, at 9 pm,

again the site is too slow from this afternoon 12PM. Even a single module  in the course page is taking minutes to load. Can you please check if there are any background jobs running?

This is affecting our schedule as we wanted to open a new module tomorrow and we are stuck for long time today and unable to make it up.

After a long time I am getting this (Error writing to database popup) - when trying to create a quiz (just a duplicate of an existing one)

Yes, Moodle runs lots of background jobs all the time. You can check these by going to 
https://ourdomain.org/admin/tasklogs.php

Right now, the server loads are very low, but I did get a db server load high alert (85%, as against a healthy load of up to 80%) at 10:15 am yesterday.

 Additionally, if many of your users are logged in, that can also slow down your response times. One possible mitigation in such cases would be for you to lock out other users by going to Maintenance Mode
https://ourdomain.org/admin/settings.php?section=maintenancemode
choose "Enable" and save - this will prevent other users from using the server - do your maintenance tasks, then go to the same page, and "Disable" - after which other users can continue to work.

And as I have mentioned earlier, running large db (database) queries will slow down the server and or make it time out. If that happens, please break down the action you wish to take into smaller chunks. "Just making a duplicate of a quiz" can be very db intensive if the quiz has a lot of content, multimedia content, a lot of users, a lot of responses, multimedia responses, and so on.

Attempted to create a blank quiz and it fails like below. (Screen showing "recalculating grades" and error writing to database at the end.) This does not have any questions. So seriously something is wrong with the backend.

Doing a search,

Found


So, this is possibly a bug. But I cannot fix Moodle bugs for you.

Not sure how I can help you with this. I can turn off cloudflare caching so that you will get a bit longer before timeout. (Found that this is already done.)

 Then you can try putting the site in maintenance mode and then try creating the quiz. But if that also fails, I have no further ideas.

Thanks for the info bro. The site was noticeably faster a couple of weeks ago. 

Managed to create the sections for this module. Hopefully things get faster to load for users tomorrow.

Sunday, March 02, 2025

Customized Moodle app bug and solution

 Copy-pasting from a request for help in the Moodle forum - "we're facing a strange issue, and any help is welcome. The questions in the feedback module are visible when viewed on a web browser, but the questions are not visible when using a customized Moodle app. Even if we just build the Moodle app for Android without any customizations, the resulting app doesn't show the questions. "

I see now that this is a bug reported in the Bug Tracker for the app, https://tracker.moodle.org/browse/MOBILE-4698

The code in the "beta" branch has the fix, and the bug is fixed there, as I check today.

Thursday, October 24, 2024

moving multiple Moodle instances from Ubuntu 20.04 to 24.04

This is going to be a long post. As planned in an earlier post, the Moodle upgrade from 4.1 to 4.5 was to be done along with server upgrade from Ubuntu 20.04 to 24.04. I thought I would be done in a day or two, but tweaking and troubleshooting continued for a week. Then setting up backups, documentation and so on, went on for another week.

Setting up the server VMs
Instead of a bitnami MySQL server, opted for a minimal Ubuntu 24.04 VM image for both the database server as well as the php application server. 
Server types
Server-php = Standard B2s (2 vcpus, 4 GiB memory) Ubuntu 20.04
30 GB OS disk, 320 GB data disk.
Since B2s is capped at 23 Mbps, doesn't make sense to use SSD.

LMS-Database-VM = Standard B2s (2 vcpus, 4 GiB memory) Debian 10.13
(bitnami mysql)
30 GB OS disk.

Created with Ubuntu 24.04 LTS minimal.
Chose standard SSD for OS disk. No Azure backup.
Azure prompts to save the private key in pem format, saved as ppk also using puttygen.

The db vm configuration following
sudo apt install mysql-server
sudo apt install mysqltuner
sudo apt install mysql-client

sudo nano  /etc/ssh/sshd_config
for changing the port 22
Then change the opened port in Azure portal also.
(had to reboot for changes to take effect, just restarting the ssh daemon did not work - 
 sudo ufw status
to verify ufw is not running, then rebooted.

Verified that updates are enabled - 
sudo apt-get install unattended-upgrades
showed that it is already installed.

The php server configuration:
Changed port 22 as above
Then installed apache and php
sudo apt install apache2
sudo apt install php libapache2-mod-php php-cli php-mysql
and rebooted.
As mentioned in my previous post

we have to attach the newly created disk using the portal, and then connect inside the vm

parted /dev/sda
mklabel gpt

(parted) mkpart                                                          
Partition name?  []? lmsdata                                              
File system type?  [ext2]? ext4                                          
Start?                                                                    
Start? 0%                                                                
End? 100%                                                                 
(parted) print

To update fstab with UUID, used blkid to get UUID.
blkid was showing only partuuid and not uuid - because it was not formatted!

But first do mount and test
mount did not work because it was not formatted!

sudo mkfs.ext4 /dev/sda1

blkid to get UUID

nano /etc/fstab to add the line
UUID=333dd333-ca33-3333-33c7-abcdef   /var/www/our_data_disk   ext4   defaults,nofail   1   2

rebooted, OK.

Tuning Apache on php server for Moodle with

AcceptPathInfo On
in /etc/apache2/apache2.conf

Also, from previous server,
<DirectoryMatch "^/.*/\.git/">
    Require all denied
</DirectoryMatch>


Also, if needed,
4 Handling 40x errors
5 Hiding internal paths 

PHP setup and installing the required extensions:
From
1. All the settings were OK in /etc/php/8.3/apache2/php.ini
except post_max_size and upload_max_filesize, set to 64M from 2M

2. extensions - installed by
sudo apt install php8.3-curl
etc
sudo apt install php8.3-ctype
sudo apt install php8.3-curl
sudo apt install php8.3-dom
sudo apt install php8.3-gd
sudo apt install php8.3-iconv
sudo apt install php8.3-intl
sudo apt install php8.3-json
sudo apt install php8.3-mbstring
sudo apt install php8.3-pcre
sudo apt install php8.3-simplexml
sudo apt install php8.3-spl
sudo apt install php8.3-xml
sudo apt install php8.3-zip
sudo apt install php8.3-ctype
sudo apt install php8.3-curl
sudo apt install php8.3-dom
sudo apt install php8.3-gd
sudo apt install php8.3-iconv
sudo apt install php8.3-intl
sudo apt install php8.3-json
sudo apt install php8.3-mbstring
sudo apt install php8.3-pcre
sudo apt install php8.3-simplexml
sudo apt install php8.3-spl
sudo apt install php8.3-xml
sudo apt install php8.3-mysql
# for CMS, we need postgres also
sudo apt-get install php8.3-pgsql
#and restart apache with
sudo systemctl restart apache2

A good guide for LAMP installation - https://www.digitalocean.com/community/tutorials/how-to-install-lamp-stack-on-ubuntu

We need postgres for CMS. So,
sudo apt install postgresql
 edit the file for changes if any needed
nano /etc/postgresql/*/main/postgresql.conf
checking the old server for the values, no changes to be made; 
listen addresses also did not need to be changed, since localhost is enough for us. 
Initially I had thought of using rclone over ssh to copy the files. But, without specifying pem file means we need to create a config file

so, just copied the script over with filezilla.

Next, rclone over ssh

Unfortunately,
rclone lsd php2024:
2024/10/08 04:50:01 Failed to create file system for "php2024:": failed to parse private key file: ssh: no key found

says check the config file.
rclone config file

The config file had both key_file and key_pem set. So, deleted key_file line.
But ... doesn't work.

So, trying with sftp.

sftp  -P 8800 -i key.pem username@ipaddress
put -r directoryname
works, but all timestamps are put to todays.

scp can retain timestamps
so using that with screen
scp -rpC sourceDirName username@server:destDirName

scp -P 8800 -i /home/ouruser/our_key.pem -rpC sourcedir remoteuser@remote.domain:/home/remoteuser/destdir

Need to change permissions and owners as per old server.

changed to 
chmod 755  moodledata
chown -R www-data:www-data  moodledata
etc. 

Takes a few minutes for our biggest instance with >100GB of data.

For the moodle code directories, I just copied within the remote machine instead of copying the identical code from the old server. 
to move hidden directories also,
mv -f /path/subfolder/{.,}* /path/

and to try rsync, 
rsync: command not found
rsync: connection unexpectedly closed (0 bytes received so far) [sender]
rsync error: error in rsync protocol data stream (code 12) at io.c(235) [sender=3.1.3]

Solution - install rsync on remote machine - 

$sites-available$ rsync -av -e 'ssh -p 8800 -i /home/ouruser/our_key.pem' . ouruser@52.172.103.81:/home/ouruser/varwww

But for certificates in /etc/letsencrypt, we can't use this, since it has to be done as root.

So,
 zip -r /home/ouruser/le.zip letsencrypt


For other instances, just copying files between directories - 

cp -rT sourcedir destdir

Added the autossh server user as per this post, for database connections over ssh tunnel. Initial setup - on remote server, 
sudo adduser theserveruser
and then when copied the public key to authorized_keys, had to nano as root and edit it, removing carriage returns. Then it worked.

But found that autossh wasn't restarting the tunnel when the server was rebooted. We need to do this - https://stackoverflow.com/questions/6758897/how-to-set-up-an-automatic-restart-of-a-background-ssh-tunnel
sudo systemctl enable our-tunnel-service-name

Copied root crontab.
sudo su -
apt install cron
crontab -e
for pg_dump
sudo su postgres
pg_dump ourdb > /tmp/ourdbdump.sql


Checking the usernames in root server directory
pg_service.conf which is readable only by root.

sudo su postgres
psql
create database dbname;
create user username;
(To change, alter user username with encrypted password 'MyPassWord';)
grant all privileges on database dbname to username;

But tried psql -U ourdb_admin -d ourdb < tmp/ourdbdump.sql -W
and got
error, 
psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: FATAL: Peer authentication failed for user "ourdb_admin"
tried adding a line to
sudo nano /etc/postgresql/16/main/pg_hba.conf
local   all             all                                     md5
and then
 sudo systemctl restart postgresql

psql -h 127.0.0.1 -U ourdb_admin -d ourdb < tmp/ourdbdump.sql -W

But lots of errors.

Did again as postgres user. Still, errors like role read_only_user does not exist etc. 

When try to open with dbeaver using the admin user,
 permission denied for schema postgres

So, need to do even after grant all privileges,
Step 1
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA name_schema TO name_user;

Step 2
GRANT USAGE ON SCHEMA name_schema TO name_user;


But, when trying that,
ERROR:  schema "postgres" does not exist
postgresql - How to switch databases in psql? - Stack Overflow
we should use \connect ourdb

list users with \du 

list schemas with
\dnS

Maybe should drop database and do again with --no-owner
drop database ourdb;

pg_dump --no-owner --dbname=postgresql://ourdb_admin:ourdbpw@localhost:5432/ourdb > ourdbdumpasadmin.sql

pg_dump: error: query failed: ERROR:  permission denied for sequence batch_job_execution_seq
alter role ourdb_admin superuser;
That worked.

Then, trying that dump in new server:
First 
create database ourdb;
alter role ourdb_admin superuser;
create user admin_readonly;

GRANT CONNECT ON DATABASE mydb TO xxx;
-- This assumes you're actually connected to mydb..
GRANT USAGE ON SCHEMA public TO xxx;
GRANT SELECT ON mytable TO xxx;< want all tables
ALTER DEFAULT PRIVILEGES IN SCHEMA public
   GRANT SELECT ON TABLES TO xxx;
alter default privileges in schema postgres grant select on tables to admin_readonly;

Next, the mysql databases.
First, set up db users.

At first, Can't connect to MySQL server on '127.0.0.1:3306'
Checking the tunnel,
sudo service tunnel status
× tunnel.service - 
---snip---
 autossh[14025]: Host key verification failed.
Solution was to sudo ssh into the host, so that the host key was stored in /root/.ssh
Then, restart autossh, worked.

CREATE DATABASE master_db;
CREATE USER 'admin'@'%' IDENTIFIED BY 'thepassword';
(did not do ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';)
GRANT ALL ON master_db.* TO 'admin'@'%';

Used the existing backup script on existing mysql server to take the backup,
$MYSQLDUMP --defaults-extra-file=/home/ouruser/.my.cnf -u $MyUSER -h $MyHOST -p$MyPASS $db | $GZIP -9 > $FILE

gzip -d thefile.gz
date;mysql -h 127.0.0.1 -u db_admin -p master_db < Database-VM.09-10-2024;date
showed that our biggest instance took around 2 hours 15 minutes to get restored.
After copying over the config files and creating the home directories of the various websites, tried enabling one of the sites and got errors.
systemctl reload apache2      Job for apache2.service failed.
See "systemctl status apache2.service" and "journalctl -xeu apache2.service" for details.
root# systemctl status apache2.service
● apache2.service - The Apache HTTP Server
---snip---     
 apachectl[8721]: Invalid command 'RewriteEngine', perhaps misspelled or defined by a module not included in the server configuration
Oct 08 15:22:40 SSSVV-php-2024 systemd[1]: apache2.service: Control process exited, code=exited, status=1/FAILURE
Oct 08 15:22:40 SSSVV-php-2024 systemd[1]: Reload failed for apache2.service - The Apache HTTP Server.


a2enmod rewrite

Solved. Then, checking with a browser, testing by editing
/etc/hosts on Linux and
C:\Windows\System32\drivers\etc\hosts
on Windows.
The browser was getting "connection refused".
Trying the upgrade directly from 4.1 to 4.5 using git, ran into some errors. Following this - https://hnsws.blogspot.com/2021/08/detailed-steps-for-moodle-upgrade-using.html
sudo apt install git
git branch --track MOODLE_405_STABLE origin/MOODLE_405_STABLE
git checkout MOODLE_405_STABLE
sudo chown -R azureuser:www-data *
sudo -u www-data /usr/bin/php admin/cli/maintenance.php --enable
sudo chmod -R 775 .
sudo -u www-data /usr/bin/php admin/cli/upgrade.php

First error was:
== Environment ==
!! max_input_vars !!
[System] this test must pass - PHP setting max_input_vars must be at least 5000. 
Corrected that with
Setting max_input_vars to 9000 in the ini files - need to uncomment also -
sudo nano /etc/php/8.3/apache2/php.ini
and
sudo nano /etc/php/8.3/cli/php.ini

Then, 
!! Asynchronous backups disabled !!
[System] if this test fails, it indicates a potential problem - Your site is currently configured to use synchronous backups. Asynchronous
backups provide a better user experience, allowing users to do other
operations while a backup or restore is in progress. Asynchronous backups
are enabled for all sites from Moodle 4.5 LTS. Synchronous backups will be
removed from Moodle LMS the version after 4.5 LTS.

OK - we can enable that after finishing the upgrade.

But then, 
Database transaction aborted automatically in upgrade.php
Default exception handler: Exception - Undefined constant "MESSAGE_DEFAULT_LOGGEDIN" Debug:
Error code: generalexceptionmessage
* line 37 of /mod/hvp/db/messages.php: Error thrown

Tried deleting mod_hvp plugin directory and then upgrading. Reports successfully upgraded from 4.5 to 4.5. But on disabling maintenance mode, home page shows 500 error. So, trying putting back version 4.1 - then drop db and restore db again.

sudo mysql -p
Enter password:
Welcome to the MySQL monitor. 

DROP DATABASE master_db;
Query OK, 563 rows affected (19.94 sec)

mysql> CREATE DATABASE master_db;
Query OK, 1 row affected (0.03 sec)

mysql> GRANT ALL ON master_db.* TO 'db_admin'@'%';
Query OK, 0 rows affected (0.02 sec)

10 minutes for importing this db, of our smallest instance.

mod_hvp supported only in 4.4 So upgrading only to 4.4. Again, error 500 in moove theme, so trying upgrade to 4.4 with moove plugin directory deleted. Upgrade to 4.4 successful.

Downloaded and installed the latest Moove theme. Added to
Moove | Custom theme settings | Appearance | Administration |
server/admin/settings.php?section=themesettingmoove#theme_moove_advanced
Raw SCSS
footer#page-footer .madeby  {display:none;}
to remove the intrusive ad.

mod_hvp upgraded to 4.4
Gave message about php cache being 2M, need to increase to >5M.
Checking in php.ini for 2M, did not find any.

Then, copied this directory to the other moodle install directories - along with the hidden files like .git directory etc, instead of doing git clone.
cp -r ../firstInstance/* .
cp -r ../firstInstance/.* .

Then edited config.php to point to the appropriate db for each instance, copied over missing files like html files, customsearch, etc - verified by doing an 
ls > textfile.txt
on old server as well as new server, and doing a diff of the two textfiles to check for missing files.

Verified mail setup on moodle was working, by sending a test email. It went to spam for both old server as well as new server, but XOAuth-based mail send working.
 
Enabled Async backups - According to
https://tracker.moodle.org/browse/MDL-81172
Site administrations > Courses > Asynchronous backup/restore
Confirm Asynchronous backups are still disabled
Enable Asynchronous backups
Enabled all the tickboxes in
admin/settings.php?section=asyncgeneralsettings
The very next day, users were unable to log in. Found that the database server had run out of disk space. logbin files were the culprit. 
But the bin log is already commented out. 
/etc/mysql/my.cnf includes
mysql.conf.d/mysql.cnf where it is commented out.

With this version of MySQL, binary logging is enabled by default - Mysql bin log is enabled by default,
and would need some startup command to not do binlogs

For purging, (I had just directly deleted)
we need to use the above syntax.

We need to specify skip-log-bin to prevent binary logs - 
# nano /etc/mysql/mysql.conf.d/mysqld.cnf
and added skip-log-bin near the end,
# disable binary log
skip-log-bin
# server-id             = 1
# log_bin                       = /var/log/mysql/mysql-bin.log
# binlog_expire_logs_seconds    = 2592000

and then restarted with sudo systemctl restart mysql.service
The newly created instance, which I had cloned from the smallest one of our existing instances, had around 2000 users which needed to be deleted. Doing bulk delete from the web gui was timing out. Doing one by one, saw that each delete would take around 10 seconds from the web ui. Bulk delete of 10 at a time would be possible, but doing that 200 times would be a pain. So, decided to do user delete using moosh, writing a script to wait for a second after each user was deleted to avoid overloading the database server. 
 
A script created by chatgpt for deleting, as well as this line 
 while read in; do echo moosh -n user-delete "$in";sleep 2 | bash; done < todelete.txt
suggested by this thread - https://stackoverflow.com/questions/61670610/bash-script-executing-command-with-file-lines-as-parameters
would fail, saying user not found. Even supplying the command-line parameters -n and - user directory would indicate user not found. Finally, just ran a script from the moodle directory, which just had lines like
moosh -n user-delete theusername@wewantto.del ; sleep 2;
moosh -n user-delete theusername2@wewantto.del ; sleep 2;
etc - that worked. Most probably the issues were due to some sort of bash variables quoting problem, I guess. Ran it using screen for some 15 hours. The script progress was interrupted twice by two users who could not be deleted without timeouts. Just skipped those users and deleted the rest. 
The cloned Moodle instance was showing a 'modify registration' screen instead of 'new registration' screen (just to remove the annoying 'Your site is not registered' message). 
https://docs.moodle.org/405/en/Site_registration#I_can't_register_my_cloned_site

1. Remove the site identifier from the database:
Delete from {config} where name = 'siteidentifier';
delete from {registration_hubs};

2. Clear the cache:
php admin/cli/purge_caches.php

3. Attempt a new manual registration (admin/registration/index.php) 

But even after doing this, perhaps due to cloudflare caching, it was going back to the 'modify registration' page. Finally, tried to copy-paste the new token in the submit url, it says invalid token. But now when I registered again, it showed old site as our cloned site and new site also as our cloned site, so I clicked on move registration to new site, and registration worked. So maybe the token replacement in the url worked.
The web server was allowing directory listings by default. To disable this, first, tried editing apache2.conf

<Directory /var/www/>
        Options -Indexes FollowSymLinks
instead of

<Directory /var/www/>
        Options Indexes FollowSymLinks

Caused apache2 restart to fail, so put it back. It turns out it needs to be
Options -Indexes +FollowSymLinks

Then it works.
The database server was showing a load average of 8 or 9 when it had a 2 CPU safe value of 1.5 or so. This was leading to slow performance. First, checked for slow queries - https://hnsws.blogspot.com/2024/07/moodle-mysql-database-cpu-usage-at-200.html

set global slow_query_log_file ='/var/log/mysql/slow-query.log';
set global slow_query_log = 'ON';
show variables like '%slow%';

tail -f /var/log/mysql/slow-query.log

did not show any. Disabled the binary logs as noted above. The problem still persists with 150 to 180% cpu usage for mysql. Took several instances out of proxying via cloudflare - that made the sites load a bit snappier. But the database is still overloaded. 
 
Checking Tasks in the biggest Moodle instance,  I see that there is a scheduled task, scheduled as "ASAP" so that it starts running as soon as it finishes,

Issue certificates task
\mod_customcert\task\issue_certificates_task

This is doing heavy database reads - > 27K records, and taking nearly 5 minutes to complete. And once it is done, it starts again.

We can either
(a) clean up the database so that only a few certificates remain - probably not desirable, since we would need the teachers / students to retain their certificates in their accounts
(b) change the scheduling to only once a day, at midnight

According to the docs, the scheduled task emails the certificate to the users / teachers if they have not yet received it.
https://docs.moodle.org/403/en/Custom_certificate_module

So hopefully it should be fine if I change the scheduling to only once a day in the middle of the night - so I did that. 

Then, disabling superfluous notifications - https://moodle.org/mod/forum/discuss.php?d=440290
server/admin/message.php
(Site Administration > General > Messaging > Notification settings )

That is the page where we can disable all email / mobile / web notifications and also disable new login notification. So, disabled new login notifications for every instance. I asked if I can turn off all notifications except email notifications for password reset etc for the five instances, but apparently they need the notifications to increase user engagement. 

Then, like I did previously, I cleared the task_adhoc table of old entries. The load average has dropped from 8.5 to 2.5, but need it to go below 1.5.

So, went through the task log on each moodle instance and see which task takes 15 seconds or more - Site Administration > Server > Tasks > Task logs - and later, any task > 1 second.

Our biggest instance - 
Category enrolment sync task - 3 sec

From https://docs.moodle.org/401/en/Category_enrolments
Warning: The use of the category enrolments plugin may cause performance problems. If so, it is recommended that you use Cohort sync instead, though this will require a cohort sync enrolment method to be added to each course in the category.

Changed this from * to */5 (every 5 minutes.) in Site Administration > Server > Tasks > Scheduled tasks.

Award badge - currently set to */5,  changed to */7
sending badges - */5 changed to */9

Then, an instance with lots of users - 
Update Course Progress Data 18 sec
*/5 -> */11
Global search indexing 10 sec
*/30 -> */43
Analytics cleanup 10 sec
Once an hour, left it as is.
 
Then the newly cloned instance - 
Global search indexing 2 sec
*/30 -> */47
Analytics cleanup 7 sec
Once an hour, left it as is.
 
Then, the development instance - 
Cleanup old sessions 2sec
Changed from * to */5
 
Then the instance we're running for a sister org - 
All tasks report failed, with the reason OAuth token refresh failed.
This is probably due to a password change - would need to put in the correct password and reconnect to that system account, in Site Administration > Server > Server > OAuth 2 services

After doing all this, plus deleting of unnecessary users in the newly created instance, the db server load average came down to 0.6 or so.

Edit: Also did the MySQL optimizations noted in a separate post on tuning mysql for Moodle.
The largest of our Moodle instances was complaining of failed tasks - send user notifications - every day, once the Tasks max fail delay time was being exceeded. And there were no send user notifications tasks which showed success at all, in the task logs. Checking email sending with a test email - works fine. So, it was probably the mobile messaging notifications which were the culprit. For starting up our customized airnotifier instance. 
sudo apt install nodejs
sudo apt install npm
sudo npm install forever -g

then tried startscript.sh - seemed to work.

On top, node briefly showed up.
sudo apt install net-tools
sudo netstat -tunlp

Node seems to be listening on 8080.

seems to indicate that if email is working, and sending notifications to mobile is working, this error should go away.

Just now enabled mobile notifications from
Site admin -> General -> Notification settings

Tested with mobile app - working.

But when I tried killing node and restarting the process using a cron, after a few hours, node showed CPU usage. Probably that was the wrong way to do it - should maybe have done a server reboot, because that was probably the reason for the node CPU usage. Did not have to go through all the possibilities as in this post - https://stackoverflow.com/questions/13375735/node-js-cpu-100 - because the log file itself had the reason.

In the log file, there were lots of entries like
errorInfo: {
    code: 'messaging/registration-token-not-registered',
    message: 'Requested entity was not found.'
  },
"So apparently, this error happens when the FCM token I'm trying to send to is not registered anymore, as evidenced by the "messaging/registration-token-not-registered" error code. In that case I just need to remove this token from the user's token and be done with it."

Then, the err.log shows "Error: listen EADDRINUSE: address already in use :::8080 - maybe that was the culprit. Rebooting the server solved the issue.
I wanted to use cloudflare proxying and cloudflare origin certificates for all the instances as in https://hnsws.blogspot.com/2022/11/cloudflare-origin-server-certificate.html . But unfortunately, with cloudflare proxying, the server was timing out for certain admin tasks like bulk-adding users, uploading, etc. within two minutes. Disabled Cloudflare proxying, put back the LetsEncrypt certificates, then
in php.ini, set max_execution_time to 600 (10 minutes) instead of 30 (the default) as suggested by Moodle and also the timeout value in /etc/apache2/apache2.conf - it was already set to 300, bumped up to 600. When proxied via cloudflare, still times out in two minutes though the official limit is 400 seconds - Cloudflare connection limits
https://developers.cloudflare.com/fundamentals/reference/connection-limits/ . So, no proxying. Then, timeout is around 10 minutes, which is manageable.
sudo apt install certbot python3-certbot-apache

https://www.digitalocean.com/community/tutorials/how-to-secure-apache-with-let-s-encrypt-on-ubuntu-20-04

But errors like
Renewal configuration file /etc/letsencrypt/renewal/name.org.conf is broken.
The error was: expected /etc/letsencrypt/live/name.org/cert.pem to be a symlink
Skipping.
Maybe the way to fix is to delete the letsencrypt directory and try again.
So, tried renaming the /etc/letsencrypt directory and re-running 
sudo certbot --apache
After apache crashed initially with errors that the file /etc/letsencrypt/options-ssl-apache.conf was not found, just disabled all the ssl sites with
sudo a2dissite *ssl*
sudo service apache2 restart
sudo certbot --apache

All OK.
The database backups could be set up without issues by following the method at https://hnsws.blogspot.com/2023/01/backing-up-mysql-databases-to-google.html
The moodledata backups did not go so smoothly. Initial test with a smaller instance - using rclone sync to copy and sync the moodledata to a shared google drive - was taking more than an hour to do the sync after copying all the files - so this would not be practical for our large instance. Then, internet searches led to the thought of trying restic. Restic with rclone to Google Shared Drive worked for the smaller instance, but failed for the larger instance. Probably due to too many retries with google rate limiting. So, the options were to use restic with ssh to a local machine, or restic and Azure Blob Storage. That worked, with the caveat that I had to use the user-level key and not a SAS token - with SAS token, the repo creation would fail.

Instead of trying ssh and local server, first tried using azure blob storage, since that is easy to set up.

Set up a new blob container with private access only, inside our existing storage account.
indicates that we get the storage access key from
Azure portal -> Storage Account -> Security + networking -> Access keys
key1 & key2 - can show key and connection string

Regenerated key1 and key2 (last used 1200+ days ago.)

But we need SAS - searching for SAS in the account, we find
Shared access signature under Security + networking.
- changed the expiry date, enabled Container access also, and clicked generate SAS and connection string.

Initial trial with the version 0.16.4 of restic installed by apt on Ubuntu 24 - 
restic -r azure:our-container:/our-repo init
Invalid backend error,
need to use the official binary.

For using the official binary, just unzipped it, renamed it and overwrote the existing binary - https://superuser.com/questions/480950/how-to-decompress-a-bz2-file
 bzip2 -d restic_0.17.1_linux_amd64.bz2
chmod +x restic_0.17*
which restic
 sudo mv  restic_0.17* /usr/bin/restic
restic version

Workaround - use the account key just to do the init, and the sas token thereafter?

So, SAS token doesn't work. But account key works. And our largest instance initial backup, 100+ GB, completed in just over an hour. So, this would be the way to go. Will add more details soon.
The email sending credentials with a php script were not working initially, so set up ssmtp on both the php server and the db server, with email notification for disk space use using bash scripts.
sudo apt install ssmtp
sudo apt install mail-utils 

While testing the mail command on the command line, if Ctrl-D doesn't work (like on Mac systems?), we can use the syntax 
echo "[email body]" | mail -s "[subject]" [recipient]

changed from php disk alert to bash disk alert.
After a day, our largest instance started showing 'Failed task - convert submission' in admin alerts. This seems to be assignfeedback_editpdf\task\convert_submission - 

Simply
sudo apt install ghostscript
and a restart of apache or reboot just to make sure, and the problem went away. 

Monday, July 15, 2024

deployment-target in cordova moodle app config.xml file

Initially, I thought the 

<preference name="deployment-target" value="13.0" />

in the moodle app's config.xml file referred to Android version, so it should be updated to 14.0 for the SDK target 34 upgrade.

But as pointed out by @dpalou, this refers to iOS version, nothing to do with Android. 

Wednesday, July 10, 2024

node version change in building customized Moodle app

Node version change, as we can see from this line in package.json for the moodle app,

so, changed this line in our build workflow.

Sunday, January 28, 2024

Google phasing out password-based SMTP / POP3 / IMAP

We got an email with "[Action Required]", with a link to this blog post from google - https://workspaceupdates.googleblog.com/2023/09/winding-down-google-sync-and-less-secure-apps-support.html

We have used two different methods to deal with this - one solution using XOAuth, and one using App Passwords.

1. For some of our Moodle instances which used Google Workspace emails for outbound emails, we could use the in-built XOAuth support as explained here,

Admins can enable Gmail XOauth2 for outgoing and incoming mail

So I created two separate sets of credentials for one of the email ids, since the redirect url for each (one production and one development) server would be different as mentioned in the documentation linked from the tracker link above -

server.url/admin/oauth2callback.php

Setting up OAuth 2.0 - API Console Help (google.com)

2. For our internal server using ssmtp as mentioned in this earlier post, this procedure (or the use of PHPMailer which supports XOAuth) would not be suitable, since it does not have a public-facing website url.

(
ssmtp seems to be orphaned since 2019, 

msmtp doesn't seem to have oauth,

describes a python solution, but needs refresh, and needs a browser.

)

In passing, the email from google mentions that App Passwords are not going away - "Password-based access (with the exception of App Passwords) will no longer be supported" . We had earlier created App Passwords for use with We can create App Passwords if we enable 2FA. So, we enabled 2FA, created an App Password, and just replaced the password in our earlier ssmtp configuration file with the App Password (without spaces). And that works.

 

Tuesday, November 14, 2023

moodle app build for v4.3.0

Some small changes were needed in the github workflow in order to build the latest version, 4.3.0, of the moodle android app. My earlier posts about building the moodle app can be found here

Trying the old workflow led to some errors like

Failed to restore plugin "cordova-plugin-screen-orientation". You might need to try adding it again. Error: Error: Command failed with EACCES

Checking the testing.yml in the 4.3.0 tag found some extra lines like 

npm ci --no-audit --prefix cordova-plugin-moodleapp

Adding that led to a different error,

 cordova-plugin-moodleapp@0.0.0 prod /home/runner/work/LMSapp/LMSapp/cordova-plugin-moodleapp

> NODE_ENV=production scripts/build.js

sh: 1: scripts/build.js: Permission denied

Trying the simplistic solution of giving run permissions for everything,

chmod -R +x ./*

This worked, except for needing resources/android/icon-background.png to be added manually as in earlier releases. 

Tweaked the build script to directly pull the codebase of the release from github instead of manually adding it, with all customizations from our private repo which has the following file structure:



 

 

 

Monday, November 13, 2023

moodle app ui for multiple logins

Once logged in to one site, if someone has a login in another site also, they can log in to the other site by tapping on their profile icon on the top right corner of the screen and choosing the "Switch account" option, even without logging out completely.

Sunday, October 22, 2023

app for new Moodle instance - several options

There was a request for an app for a new Moodle instance. My response was, in part,

There are several options. 

1. Option within the same app - We could push out an update to our customized Moodle app which would enable users to choose between the two Moodle sites - this is fairly easy to implement.

2. Separate app - Having a separate app is certainly do-able. If we just change the name of the app, URL and (if necessary) the splash screen and icon, we could have a separate app. If you want that, please let me know the name of the app you wish to have, and if you want any changes to the splash screen and icon.

3. Web app - If you don't want the hassle of maintaining another app, we can use a technique like this,
and have a qr code which students can scan, and then install the site as a web app to their home screen.

(Since the courses on this site are supposed to be interactive, some elements may not work on the mobile app, but might work on the web app. Comparison table is at
The difference would be that the web app will save downloaded files outside the app, in the downloads folder.)

Edit - Option (1) was chosen.

Saturday, June 10, 2023

moodle app build blues and fix

After my previous post about building the moodle app using Github actions, Moodle app version 4.1.1 would not build with the same commands. I guess there was some compatibility issues with the versions of cordova or gradle or SDK or .... 

Anyway, the newly released Moodle app version 4.2.0 builds without any issues with this basic workflow. I had seen an announcement that cordova-android is going to default to the latest version in future, so the workflow did not have to specify cordova-android@12.0.0.

Edit: 13 Jan - This commit forces a change to the moodle.config.json format. The Moodle tracker does not have any documentation about the expected format. By trial and error, found that this format works.

"sites": [
    {
      "name": "This seems to be ignored",
      "url": "https://our.moodle.instance.org"
    }
  ],

Sunday, January 08, 2023

Moodle server issues, possible reasons and fixes

Copy-pasting from emails:

1. In November,

... had mentioned a temporary connection failure with the server when running ad-hoc queries. This could most probably have been due to some query overloading the database momentarily.

But when checking the logs, I find repeated "Web service authentication failed" messages, spaced every few minutes, from a set of regularly spaced IP addresses.

This means that our server is under distributed brute force attack. Which means that (a most-probably automated) attacker is using many different computers or devices to guess passwords (in this case web service tokens) by trying all possible combinations.

This sort of thing is common for SSH ports, but this particular attack seems to be tailored to Moodle. One way to block this particular attack would be to disable web services, but that would block the mobile app also. Will look for other solutions, may be asking in Moodle forums.

Not something which we need to worry too much about, just something we need to do to make the server a little safer.

2. Disabling "login from new device" notification emails. Disabled sending emails saying "new login from -- device",
from https://our.server.org/admin/message.php
"New login notifications"

3. Send only Digest emails from Forums -
The forum email notification setting seems to be off already. Perhaps the emails are due to someone deliberately sending a message to all forum users,
Bulk user actions - MoodleDocs
or something like that?

4. Stop send email altogether to specific users, like the bouncing users above - I have set
emailstop=1
for the user bouncinguser@somedomain.org

But I'm seeing bounced messages to noreply@someotherdomainofours.org - I'm not sure where this non-existent email id has been entered. It must be somewhere in the settings, instead of noreply@correctdomain.org entered as noreply@someotherdomainofours.org - but I could not trace out where.

 


Monday, December 26, 2022

Notes on building release apk using Github Actions - cordova commandline build

  1. The working workflow file for building the modified version of the Moodle app version 4.1.0 is at buildSSSVV.yml

  2. Though the main idea was to clone a private repo similar to this post, one difference was that I used my user's private key instead of adding a separate key-pair as deployment key - that also worked, but I should be careful to remove my private key from the destination repo as soon as I finish my builds.

  3. We must clone the private repo and build the public repo as part of the same job in the github workflow, or else the file would be cleaned up and not available (unless stored as an artifact.) 

  4. One stumbling block preventing builds on the first attempt was to find the correct syntax for the release build using cordova build. Turns out it needs several points which were not clearly discussed at cordova's documentation example, but was mentioned in ionic's documentation example. (Edit - all the flags required are mentioned at https://cordova.apache.org/docs/en/11.x/guide/platforms/android/index.html#using-flags - I had not seen this page earlier.) I did not create a build.json since the commandline switches worked for me. "failed to read key from store" was because I missed the --password parameter, I had put in only the --storePassword parameter. The error was not due to requiring escaping of special characters in the password.

  5. We also need -- -- twice in order to get an apk instead of an aab as the output file, Cordova generate release apk instead of aab - Stack Overflow.

  6. The final correct command to build (at least for this version of cordova android) was
    npx ionic cordova build android --release -- -- --packageType=apk --keystore="~/sssvv_mobile-release-key.keystore" --password=$KEY_PW --storePassword=$KEY_PW --alias=$KEY_ALIAS

  7. Before trying all this, I tried building just an unsigned apk - but jarsigner signing of the unsigned release apk didn't work. I wonder why.
    jarsigner -keystore <keystore_file>  -storepass <storepass> -keypass <keypass> <unsigned_apk_file> <alias_name>
    The apk was not showing the correct icon, and "problem parsing file" when try to install. Maybe this problem is due to some version difference between the java / SDK / build tools on the build machine and the signing machine? No idea.

  8. The technique of using this GUI tool to avoid typing out all the paths - Android APK Signer & Aligner - Luke Alderton - also failed, probably due to the same issue as in the above point, whatever that is. Which is why I went ahead with trying to generate the signed release apk from the workflow itself, as above.

Tuesday, December 20, 2022

ad-hoc reports - device information

Copy-pasting from an email exchange regarding the addition of device information to files downloaded ad-hoc report on Moodle:

the device used by the user whether tablets supplied by us or smartphones

This information is not directly captured in the logs, so we cannot put this directly in the files downloaded per user report.

But we do have the
prefix_user_devices table,
which logs which mobile device a user has logged in from, including the time-stamp of when they first logged in from that device.

So, we could write a separate queries for things like:
1. How many users are using the tablets provided by us
2. How many users are using mobile phones or other tablets
3. Which devices a particular user has used, including the time periods

and so on. 

Also, we must note:
This table would not have entries if the user has logged in using a web browser - even if it was a tablet or mobile. Only those devices which use the Moodle app or our mobile app would be logged in this table.

Sunday, November 20, 2022

Google Play store blues - trying in vain to update our app - shortcut to update version code

Updates to one of our apps was being blocked by Google Play console.

Initially, in the developer console, there was a Policy Declaration we had to make, stating the reason for adding a particular sensitive permission - in this case, REQUEST_INSTALL_PACKAGES permission.

There was a form to appeal the rejection, but submitting that just gave a generic reply saying the appeal was rejected.

The Moodle app's repository meanwhile mentioned this issue, and gave a fix - https://tracker.moodle.org/projects/MOBILE/issues/MOBILE-4134?filter=doneissues

But even after uploading a version of the app with these fixes, the same generic automated rejection of the update. Then I tried the "reaching out to our policy support team" link at the bottom of the email. 

It was not very clear where exactly I should reach out to the policy support team in this screen, but I guessed it was via the arrow to the right of the "Appeals" section. I specifically mentioned that this permission had been removed, but the "policy declaration" could not be removed - there is no provision on the console to do that.

Again I got a template email as a reply saying "we are unable to approve your app's use of REQUEST_INSTALL_PACKAGES", but at the bottom of that email, there was some additional information -

Step 2: Submit a compliant update or remove the permission from your app

Once you’re ready to submit a compliant version of your app:

  1. Make the necessary updates to address the issue(s) identified above OR please remove the permission from your manifest AND in-app functionality.
  2. Double check that your app is compliant with all other Developer Program Policies.
  3. Sign in to your Play Console, upload the modified, policy compliant APK across all tracks, and deactivate the non-compliant APK(s). 
    1. To deactivate a non-compliant APK, please create a new release and upload a compliant APK to the same track.
    2. Be sure to increment the APK version number and set the release to 100% rollout, in order to successfully override and deactivate the non-compliant APK. 
  4. To save any changes you make to your release, select Save.
  5. When you've finished preparing your release, select Review release.

Kindly note that your changes aren't sent for review automatically. You must go to the Publishing overview page and click Send for review to submit your changes.

Aha - so I have to deactivate non-compliant APKs on all tracks - not just the Production track. And how should I deactivate?

https://stackoverflow.com/questions/48624580/how-to-deactivate-my-previous-version-retained-apk-in-play-store

So I have to go to each track and add a new version with "Create New Release". For that, I had to create one more version of the "fixed" APK, since the current "proper" release had been auto-rejected - I had to build another apk with an incremented version code. One of the rejected apks had a higher version code than the current "fixed" version. So, I added one to that to get my next version code - though retaining the same version name and version number displayed inside the app, which have to be changed via config.xml and moodle.config.json. 

For changing only the version code, I just did Edit --> Find --> "Replace in files" inside Android Studio for the old version code with the new version code, with "Scope" as the "search in" parameter. It found 7 matches in 6 files or something like that. 7 clicks of "Replace" later, clicking on "Generate signed APK" got me the fresh apk which I could upload to the Play store. Much easier than going through the entire Cordova build process. 

Now, after submitting all these tracks, it looks like the update has not been auto-rejected. I also edited the "Policy declaration" mentioning that this permission is no longer needed. Perhaps after the update is reviewed, the policy declaration change - where this permission is not listed - would also happen. We have to wait and see. Maybe a week.

Edit: The update went through in 2 days.