perf(AppConfig): Cache loading the app config from the database #53868
+66
−34
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
The entire AppConfig is loaded on every request to figure out the enabled apps and their versions (and other things as well). By putting the AppConfig into the local cache we can make the access much faster and reduce the overhead of each request. A very low TTL is used to avoid syncing issues in clustered setups. I also tested with a higher TTL, but it does not improve the performance (as long as the instance is hit by many requests per second).
During my testing of the AppConfig caching I had to disable the global cache prefix logic, as it had a circular dependency on the AppConfig. By using a hardcoded global cache prefix the performance improvement was a bit better than the final result, but it's not possible to do this on a production instance, so the global cache prefix logic had to be changed to not rely on the AppConfig.
In theory the global prefix calculation could be made even faster, by either checking if the setup is clustered (not sure if that is possible?) or by checking if no distributed cache is present. If either of them is true, then the mtimes of the appinfo/info.xml files could be used directly as the global cache prefix and it would not even be necessary to have a local cache for parsing the appinfo/info.xml. I tried this (just hardcoded, not with any of the mentioned checks) just to see if there is any actual improvement, but it was only about 1-2ms and could very well be noise in the data.
Testing setup
docker run --rm -it --network host -e POSTGRES_PASSWORD=postgres postgres:17 rm -rf data config/config.php ./occ maintenance:install --admin-pass admin --database pgsql --database-name postgres --database-host localhost --database-user postgres --database-pass postgres ./occ config:system:set memcache.local --value '\OC\Memcache\APCu' PHP_CLI_SERVER_WORKERS=100 php -S 0.0.0.0:8080 sudo tc qdisc add dev lo root handle 1:0 netem delay 10msec k6 run -e BASEURI=http://localhost:8080 basic.js sudo tc qdisc del dev lo root
Measurements
https://github.com/come-nc/k6_nc_scripts/blob/main/basic.js (with timeout disabled)
Checklist