Description
Hello,
I've encountered a bug where, at random times, Matomo resets to an unconfigured state, displaying the installation phase in the web UI.
In these cases, the config/config.ini.php
file contains only the following:
[General]
installation_first_accessed = 1740328930
After extensive investigation, I identified the root cause: a race condition triggered by using the config:set
CLI command (or any other action that writes to the configuration file).
How to reproduce
Run config:set
commands in a loop:
while true; do php console config:set --section General --key test --value 1; done
In my case, running this loop in four different terminals caused config/config.ini.php
to be erased within seconds, leaving only:
; <?php exit; ?> DO NOT REMOVE THIS LINE
; file automatically generated or modified by Matomo; you can manually override default values in global.ini.php by redefining them in this file.
[global]
test = "1"
When accessing the Matomo web UI, the installation process is triggered, and config/config.ini.php
is rewritten as follows:
; <?php exit; ?> DO NOT REMOVE THIS LINE
; file automatically generated or modified by Matomo; you can manually override default values in global.ini.php by redefining them in this file.
[General]
installation_first_accessed = 1740329172
[global]
test = 1
Root causes
1. The configuration reader does not handle locks
When writing the configuration, file_put_contents
is used with LOCK_EX
, which is good. However, the reader does not support locks.
This creates a race condition when the configuration file is being written while another process reads it. If the file is truncated during reading, Matomo may interpret it as missing installation details and re-trigger the installation process, adding installation_first_accessed
to the configuration file.
2. The sanityCheck
function doesn't handle locks and is unnecessary
The sequence of events in Matomo's configuration process is problematic:
- The configuration file is first written to disk.
- The
sanityCheck
function is then called. - This function reads the configuration file (without locking) and verifies its contents. If there is a difference, the file is rewritten.
This introduces two issues:
- The lack of locking in the
sanityCheck
function leads to the same race condition as with the reader. - The logic itself is questionable. If a write operation is performed, we should assume the OS did it. There is no apparent reason to re-read the file, and even less reason to rewrite it.
Solutions
1. Implement lock handling in the configuration reader
Matomo's configuration reader is handled by the ini
component. I have patched it to support file locking and wait for the lock to be released before reading.
See matomo-org/component-ini#28
2. Remove the sanityCheck
function
I have patched Matomo's Config
class to remove the sanityCheck
function, as it introduces a race condition and serves no useful purpose.
See #23066
I'm running these two patches and they totally solved the problem.