Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Keep possibility to disable in-memory caching of routes #57640

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mpdude opened this issue Jul 4, 2024 · 32 comments
Closed

Keep possibility to disable in-memory caching of routes #57640

mpdude opened this issue Jul 4, 2024 · 32 comments

Comments

@mpdude
Copy link
Contributor

mpdude commented Jul 4, 2024

Description

Here is one use case for the feature that was postponed in #53059. Opening as a new issue so it does not get lost in the already-closed issue. We can close this one here once #53059 is re-opened.

The use case is that it may be necessary to work with different sets of routes during functional tests. The Router keeps the routes in a private static $cache property, which makes it impossible to change the routes once one set has been loaded.

Setting $cache_dir to null disables the part of the code where routes are dumped to a cache file and the results of that file are loaded into the ::$cache property.

Example

No response

@derrabus
Copy link
Member

derrabus commented Jul 4, 2024

The code that handles this static cache is here:

private static function getCompiledRoutes(string $path): array
{
if ([] === self::$cache && \function_exists('opcache_invalidate') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOL) && (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) || filter_var(\ini_get('opcache.enable_cli'), \FILTER_VALIDATE_BOOL))) {
self::$cache = null;
}
if (null === self::$cache) {
return require $path;
}
return self::$cache[$path] ??= require $path;
}

So, as far as I understand it, this is a cache for the compiled routes that only kicks in if someone disabled Opcache (which is the default on CLI and your tests).

This basically means that you โ€“ during your functional tests โ€“ recompile the router with a different set of routes for the same kernel and environment. Does that sound about right?

@mpdude
Copy link
Contributor Author

mpdude commented Jul 4, 2024

Absolutely. Insane?ย Maybe a more precise description would be that I have a custom route loader that provides different sets of routes for various tests. The kernel is shut down between different tests, but routes persist across tests (even across different Router instances, since the container is reset as well) in the ::$cache static property.

I'd need to check, but it might be that during those tests, it makes no sense to dump the compiled routes to a file either, since they'd never be re-used... but that's just overhead, no functional difference.

@mpdude
Copy link
Contributor Author

mpdude commented Jul 4, 2024

I see that getCompiledRoutes() helps sharing the compiled route set between the Matcher and Generator code paths... but does this have to be static?

Is there a use case of sharing compiled route sets across different Router instances?

@nicolas-grekas
Copy link
Member

can't you use a different cache path for each set of routes? the code is written with the assumption that one file = one set of rules, and that assumption doesn't look insane to me

@derrabus
Copy link
Member

derrabus commented Jul 4, 2024

Sharing between those two is not the motivation. It's rather that parsing a large PHP file is expensive. This is a poor man's Opcache replacement.

And this also means: If you were to run your functional tests on a machine with Opcache enabled for CLI, you will run into similar problems. The static cache would be bypassed, but Opcache would say, "hey I've compiled this file already, I'm going to serve it from my bytecode cache!", which will screw you over just as this static cache does already.

@derrabus
Copy link
Member

derrabus commented Jul 4, 2024

I mean, how many different sets of routes are you testing against? If we're talking about two or three or four, you could maybe use different environments for them (e.g. instead of test you have test1 and test2). This way, you might even reuse caches between test runs which might speed up your functional tests significantly.

@mpdude
Copy link
Contributor Author

mpdude commented Jul 4, 2024

I'll have to think that through...

Why is it relevant to have a static property?

@derrabus
Copy link
Member

derrabus commented Jul 4, 2024

Opcache is static as well. This is as close as we can get to Opcache's behavior without actually using Opcache.

@nicolas-grekas
Copy link
Member

because if file contents are immutable, caching in a static property improves sharing of the cache

@mpdude
Copy link
Contributor Author

mpdude commented Jul 4, 2024

caching in a static property improves sharing of the cache

... for people who create different instances of the Router? Is that a use case we want to optimize for?

(Update โ€“ Maybe I understand: Test suites that use the same set of routes again and again but have no opcache enabled benefit from keeping the routes in memory that way, to achieve caching across different Router instances/tests.)

can't you use a different cache path for each set of routes?

and

And this also means: If you were to run your functional tests on a machine with Opcache enabled for CLI, you will run into similar problems.

This is why I initially made it possible to disable file-based caching entirely in #47496.

@mpdude
Copy link
Contributor Author

mpdude commented Jul 4, 2024

Only a quick, superficial check, but relevant test suites also pass when I do not set the cache_dir to null but use a non-static $cache property instead.

@mpdude
Copy link
Contributor Author

mpdude commented Jul 4, 2024

Opcache is static as well.

You mean in the sense that once the file has been loaded during a request/process, it might be configured to give the same results again when the file is required once again?

There is special code in \Symfony\Component\Config\ResourceCheckerConfigCache::write that invalidates the opcache when cache files are written.

@derrabus
Copy link
Member

derrabus commented Jul 4, 2024

There is special code in \Symfony\Component\Config\ResourceCheckerConfigCache::write that invalidates the opcache when cache files are written.

We could do the same with our static cache, right? ๐Ÿค”

@mpdude
Copy link
Contributor Author

mpdude commented Jul 4, 2024

But you don't know when the file is written.

@derrabus
Copy link
Member

derrabus commented Jul 4, 2024

But you don't know when the file is written.

Why's that?

@mpdude
Copy link
Contributor Author

mpdude commented Jul 4, 2024

I guess all I am asking for is a Router config setting to either disable file dumping/loading entirely and/or prevent caching in the static property ๐Ÿ˜‡

@mpdude
Copy link
Contributor Author

mpdude commented Jul 4, 2024

But you don't know when the file is written.

Why's that?

Ah! We call \Symfony\Component\Config\ConfigCacheInterface::write from within the Router, so that might be a suitable moment ๐Ÿ‘

@mpdude
Copy link
Contributor Author

mpdude commented Jul 4, 2024

I'll try that. Still, being able to set the cache_dir to null from within framework config would disable all the file-based overhead I don't need in the first place, but that may well be a personal itch.

@nicolas-grekas
Copy link
Member

Did you try setting the router.cache_path parameter to null?

@mpdude
Copy link
Contributor Author

mpdude commented Jul 4, 2024

I added router.cache_dir myself in #47496 ๐Ÿ˜… and forgot about it since I was so focused on configuration-based settings instead of tweaking parameters directly.

So, the idea is to remove framework.router.cache_dir as a config setting without replacement (#52962), not add a config setting to disable the file-based caching (#53059) but keep router.cache_dir around as a semi-official configuration value to achieve the same results?

Would work for me.

@derrabus
Copy link
Member

derrabus commented Jul 4, 2024

Did you try setting the router.cache_path parameter to null?

If you meant router.cache_dir, we've deprecated that in #52962.

@nicolas-grekas
Copy link
Member

we've deprecated the config option but not the parameter IIRC

@derrabus
Copy link
Member

derrabus commented Jul 4, 2024

So, the idea is to remove framework.router.cache_dir as a config setting without replacement (#52962), not add a config setting to disable the file-based caching (#53059) but keep router.cache_dir around as a semi-official configuration value to achieve the same results?

Would work for me.

I'd rather explore the path of fixing file-based caching for your scenario instead of relying on obscure ways to disable that functionality, tbh.

@mpdude
Copy link
Contributor Author

mpdude commented Jul 4, 2024

I'd rather explore the path of fixing file-based caching for your scenario

You mean #57640 (comment)? PR incoming.

Or do you mean that my test suites should use different environments and/or kernel cache dirs when working through different route configurations? I'll look into that, but seems to be a bit too complex to circumvent a feature I don't want to use in the first place (file-based caching of compiled routes).

@nicolas-grekas
Copy link
Member

an obscure solution for an obscure need might fit ;)

@mpdude
Copy link
Contributor Author

mpdude commented Jul 4, 2024

Trust me, I've spared some details to make this bearable for you guys.

@mpdude
Copy link
Contributor Author

mpdude commented Jul 4, 2024

Hope #57645 is what @derrabus was expecting.

@derrabus
Copy link
Member

derrabus commented Jul 4, 2024

Hope #57645 is what @derrabus was expecting.

Yes! ๐Ÿฅณ

@derrabus
Copy link
Member

derrabus commented Jul 4, 2024

Oh, and did you check if that change fixes your problem?

@mpdude
Copy link
Contributor Author

mpdude commented Jul 4, 2024

Only superficially, but it seems so, yes. Still, I'll set the router.cache_dir parameter to null additionally, since it entirely disables the loading/dumping code that the tests do not benefit from.

Thank you guys for the healthy discussion. As always, it was a pleasure working with you โ€“ you're always open-minded and interested in solutions, not dismissing suggestions right away.

@derrabus
Copy link
Member

derrabus commented Jul 4, 2024

Thank you guys for the healthy discussion. As always, it was a pleasure working with you โ€“ you're always open-minded and interested in solutions, not dismissing suggestions right away.

I thank you for making me dig into exciting Symfony code I haven't read for ages. ๐Ÿ˜…

@mpdude
Copy link
Contributor Author

mpdude commented Jul 4, 2024

Code that feels like ๐Ÿฅƒ to you, I hope ๐Ÿ˜‰.

symfony-splitter pushed a commit to symfony/routing that referenced this issue Jul 4, 2024
โ€ฆhe file-based cache (mpdude)

This PR was merged into the 5.4 branch.

Discussion
----------

[Routing] Discard in-memory cache of routes when writing the file-based cache

| Q             | A
| ------------- | ---
| Branch?       | 5.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Issues        | Fix #57640
| License       | MIT

The `Router` has a `private static ?array $cache` field that it uses to cache compiled route collections in the case that OpCache is not available to cache the file-based cache already. This is somewhat [a poor man's Opcache replacement](symfony/symfony#57640 (comment)), useful e. g. for speedups during (functional) tests.

`\Symfony\Component\Config\ResourceCheckerConfigCache::write` contains special code to make sure the OpCache will reload files after they have been written, but the `static` property kind of "hides" this feature.

With this PR, the `static` cache entry is cleared as well.

Commits
-------

de7d8849eb [Router]ย Discard in-memory cache of routes when writing the file-based cache
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants