silverstripe / silverstripe-framework

Silverstripe Framework, the MVC framework that powers Silverstripe CMS
https://www.silverstripe.org
BSD 3-Clause "New" or "Revised" License
719 stars 820 forks source link

Cache is not correctly shared between CLI and non-CLI #11145

Open GuySartorelli opened 4 months ago

GuySartorelli commented 4 months ago

Module version(s) affected

Probably all of 'em, but I've only checked CMS 5 so far

Description

Cache run in the CLI with the PHP ini value apc.enable_cli NOT set to true will result in different cache being used compared with non-CLI or CLI with apc.enable_cli set to true.

This can result in cache being out of sync when values are changed in one context but not in the other.

There are two causes:

  1. The main one is that APCu isn't used in CLI, which means that cache doesn't get warmed or flushed by CLI actions
  2. The other is that the CLI context uses PhpFilesAdapter, but the non-CLI context falls back to FilesystemAdapter explicitly - so they're not even sharing filesystem-level cache.

UPDATE

It turns out APCu cache just can't be warmed or cleared from the CLI. You can make curl or similar requests to the server form CLI to warm or clear the webserver APCu cache but that is kinda hacky and has some obvious drawbacks.

As I've noted in a comment below I've heard some people saying redis doesn't have this problem - it might make sense to provide support for redis in core with APCu as a fallback. At the very least we should clearly document this known and common problem with the current setup.

How to reproduce

  1. Set some config (any config) and run a flush in CLI, then load your app in the browser.
  2. Change the config value (note this only happens when changing existing config, not setting new config) and flush in the CLI only.
  3. Check /dev/config in the browser, and compare with sake dev/config in the CLI. You should see the old value in the browser, and the new value in the CLI.

Possible Solution

The second cause can be fixed right away by just using PhpFilesAdapter as the fallback in both contexts if it's supported, but the main problem needs to be fixed by Symfony. See https://github.com/symfony/symfony/issues/53962

Additional Context

This is not talking about when running CLI with a different user or PHP version than the web server, which is a known and intentional distinction.

Validations

OLD PRs

Notes

Acceptance criteria

GuySartorelli commented 4 months ago

Turns out even enabling apc.enable_cli doesn't resolve this problem... so it might be that it's a fundamental restriction with APCu itself? If that's the case, best we can do is:

  1. Make sure out CLI and non-CLI are sharing the same filesystem cache
  2. Very clearly document that with APCu cache enabled, flushing from the CLI will not flush the browser cache

I've seen anecdotal reports that redis resolves this, so maybe we should highlight that a bit, too.

elliot-sawyer commented 3 months ago

Chatting with @GuySartorelli on Slack:

I've done quite a bit of work on caching with https://github.com/pstaender/silverstripe-redis-cache and there's some good example config for forcing everything over into Redis if one was so inclined. One thing the module cannot do is force <% cached %> blocks into the cache. I still don't know where that content is stored, but I believe the class manifest is also written to the filesystem with PhpFilesAdapter

forsdahl commented 1 month ago

In my experience, PhpFilesAdapter will not be used by default in CLI in any standard PHP configuration, but it will be used by default in non-CLI conditions (since it is dependent on OPcache being enabled). This means that even without APCu, there will still be a mismatch in cache-adapters between CLI and non-CLI. Internally we have started configuring PHP so that we enable OPCache for CLI, but with very specific configuration options (since enabling OPCache for CLI is not really recommended, and with the incorrect configuration it will actually be slower). Our PHP configuration for CLI is to do the following configs:

opcache.enable_cli=1
opcache.blacklist_filename=/path/to/blacklist.txt
opcache.file_cache="/var/tmp/"
opcache.file_cache_only=1
opcache.file_cache_consistency_checks=1

That blacklist.txt file contains the path to the Silverstripe installation, so that in practice OPCache is not used at all in CLI, it is just configured to be enabled. All in all a really cumbersome way to get around the problem, but I don't see any direct way of doing this easily, except for to perhaps warm the FilesystemAdapter cache from non-CLI even though that is not the one used (which might impact performance)? Solving this would be good though, since there are a fair number of questions related to this on the Slack channel.

GuySartorelli commented 1 month ago

Need to figure out how to get the in-memory cache configured before we have the manifests, since we need it to cache the config (and therefore can't put it in config)

See https://github.com/silverstripe/silverstripe-framework/issues/11257