Skip to content

feat(AppConfig): cache the config if local cache is available - #54383

Merged
susnux merged 6 commits into
masterfrom
feat/cache-app-config
Aug 18, 2025
Merged

feat(AppConfig): cache the config if local cache is available#54383
susnux merged 6 commits into
masterfrom
feat/cache-app-config

Conversation

@susnux

@susnuxsusnux commented Aug 12, 2025

Copy link
Copy Markdown
Contributor

Summary

Benchmark

Tested flow:

  • login
  • move from dashboard to files app
  • upload a folder with 1 subfolder and each 3 files
  • in personal settings change the language
  • back in files share the uploaded folder to another user
  • add a comment on the uploaded folder
  • log out

Queries on the app config table:

  • Without caching: 140 queries (+-5)
  • With caching:
    • TTL of 3s: 25 queries (+-3)
    • TTL of 120s: 9 queries (+-1)

Checklist

@susnuxsusnux added the 2. developing Work in progress label Aug 12, 2025
@susnux
susnuxforce-pushed the feat/cache-app-config branch 2 times, most recently from e05a6f3 to d59d106CompareAugust 12, 2025 13:34
@susnuxsusnux added this to the Nextcloud 32 milestone Aug 12, 2025
@susnux
susnuxforce-pushed the feat/cache-app-config branch 10 times, most recently from 09672be to 76b96ccCompareAugust 14, 2025 08:33
@susnux
susnuxforce-pushed the feat/cache-app-config branch 3 times, most recently from 7629f91 to ae90298CompareAugust 15, 2025 15:02
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This removes a circular dependency between AppConfig and cache factory.
When a cache in the app config is used.
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
@susnux
susnuxforce-pushed the feat/cache-app-config branch from ae90298 to 503f43fCompareAugust 18, 2025 11:24
@susnux
susnux marked this pull request as ready for review August 18, 2025 12:05
@susnux
susnux requested review from a team as code ownersAugust 18, 2025 12:05
@susnux
susnux requested a review from CarlSchwanAugust 18, 2025 12:33
@skjnldsv

Copy link
Copy Markdown
Member

What about #53868? 😅

My PR has red CI, although that might be fixed by rebasing and restarting cypress.

first one to green CI wins! Ready, set, go! 🐎 🏁

@AndyScherzingerAndyScherzinger left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

@susnux
susnux merged commit 6d5dd4b into masterAug 18, 2025
218 of 222 checks passed
@susnux
susnux deleted the feat/cache-app-config branch August 18, 2025 13:12
@AndyScherzingerAndyScherzinger added 3. to review Waiting for reviews and removed 2. developing Work in progress labels Aug 18, 2025
Comment threadconfig/config.sample.php
Comment threadlib/private/AppConfig.php
Comment threadlib/private/Memcache/Factory.php
Comment threadlib/private/Memcache/Factory.php
@come-nc

Copy link
Copy Markdown
Contributor

I forgot to send my review comments -_-, they were stuck in pending.

@fancycode

Copy link
Copy Markdown
Member

This seems to break a usecase in Talk (see nextcloud/spreed#16227).

Within one request to Talk:

  1. Request A: Talk creates a random nonce and stores it using setAppValue.
  2. Request A: Talk performs a request to the HPB service backend and includes the random nonce.
  3. The HPB service performs a request to Talk (B) to authenticate the incoming request, includes the nonce.
  4. Request B: Talk reads the nonce using getAppValue, compares to the value sent by the HPB service and returns 200 or 412.
  5. The HPB service returns 200 or an error depending on the response of request B.
  6. Request A: Talk evaluates the response from the HPB service.

In some cases, the value read in step 4 is empty, so the request can not be authenticated by the HPB service.

If I set cache_app_config to false, this doesn't happen. Also this did not happen for instances before Nextcloud 32.

Thanks for @SystemKeeper for tracking this down.

@nickvergessen

Copy link
Copy Markdown
Member

The problem is setTypedValue only does $refreshCache = true when the lazy attribute changes. Which is not the case here.

I guess setting a value that is cached should $refreshCache as well?
Or is the intended case that Talk would call clearCache() manually?

@fancycode

Copy link
Copy Markdown
Member

I guess setting a value that is cached should $refreshCache as well?

IMHO that's the right thing to do. Having to clear the cache manually is a functional change that might break other affected apps, too.

@susnux

Copy link
Copy Markdown
ContributorAuthor

The problem is setTypedValue only does $refreshCache = true when the lazy attribute changes. Which is not the case here.

The local cache is also cleared for non-lazy writes:

$this->clearLocalCache();

refreshCache

This is only for removing the class local dictionary and that's not needed for non lazy as they are just added to the dictionary anyways.

@juliusknorr

juliusknorr commented Nov 18, 2025

Copy link
Copy Markdown
Member

How does this work with clustered setups? I also fear this could break in a lot more places if a config value is written on one webserver and then the old valuer is still read on another webserver. Should we recommend disabling this for clustered instances? Maybe then the default should be switched to false for the config.php option? This will be super hard to debug such issues in production scenarios. :/

@fancycode

Copy link
Copy Markdown
Member

Good point.

FWIW, our setup is not clustered but running the FPM Docker image (non-AIO).

@susnux

Copy link
Copy Markdown
ContributorAuthor

Maybe then the default should be switched to false for the config.php option?

This was discussed with product management but decided that it has great benefits for the majority and there are only some special cases.
One we discussed is having multiple application servers but for this it was decided we only support pinned sessions.
Meaning one user will have consistent behavior, sure another user could see something already changed but the TTL only a couple seconds so this should be safe just like the setting was changed a second later.

If for any reason pinned sessions are not possible (we do not recommend this) then this can manually disabled in the settings, but this only affects very edge cases.

@juliusknorr

Copy link
Copy Markdown
Member

If for any reason pinned sessions are not possible (we do not recommend this)

We always suggest either sticky sessions or redis as session storage, unless this changed recently. We have quite a few big instances not using session stickyness.

I think this shoud at least be properly documented and also noted to admins in the upgrade manuals.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3. to reviewWaiting for reviewsenhancementpending documentationThis pull request needs an associated documentation updateperformance 🚀🍂 2025-Autumn

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf(AppConfig): Cache loading the app config from the database

9 participants

@susnux@provokateurin@skjnldsv@come-nc@fancycode@nickvergessen@juliusknorr@AndyScherzinger@tobiasKaminsky