Skip to content
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

Expose opcache.validate_timestamps to Environment #510

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

aSeriousDeveloper
Copy link

@aSeriousDeveloper aSeriousDeveloper commented Jan 15, 2025

Validate Timestamps

opcache.validate_timestamps is enabled by default in PHP, and not exposed as an environment variable in this Dockerfile.

When enabled, it uses the value of opcache.revalidate_freq to check whether files should be refreshed by OPcache based on when they were last updated.

According to some sources, there are better ways of handling OPcache refreshed for development & production, however they require access to opcache.validate_timestamps.

How to use

The specific changes one could make are the following:

Development

opcache.validate_timestamps=1 # Allow checking file timestamps to refresh cache
opcache.revalidate_freq=0 # Files are to be checked for changes every request

Production

opcache.validate_timestamps=0 # Never check file timestamps to refresh OPcache, require PHP reload
opcache.revalidate_freq=0 # Ignored due to validate_timestamps being disabled

This PR

This PR exposes the value to the environment, while making sure its default value is the same as PHP's default. This means that the current way the dockerfile works should be preserved, while allowing for better customisation of OPcache to improve / preserve performance in production & utilise some performance benefits of OPcache in development.

Example

This is effectively the same as the ini examples above, except now they can be specified in a .env.

Development

PHP_OPCACHE_VALIDATE_TIMESTAMPS=1
PHP_OPCACHE_REVALIDATE_FREQ=0

Production

PHP_OPCACHE_VALIDATE_TIMESTAMPS=0
PHP_OPCACHE_REVALIDATE_FREQ=0

Other notes

Using PHP / Laravel Caching

I haven't don't a huge amount of testing of how this interacts with Laravel's caching of views, routes, etc. There's a likelihood that using this in production in Laravel will cause these caches to not be properly read, as OPcache won't check to see if they've changed.

Therefore, if you are going to attempt this, make sure you have a way to properly reset / invalidate OPcache after these Laravel caches have been generated. For example, this dependency, or simply building your own way to call opcache_reset would do the trick.

I took a closer look at the workings of the Dockerfile (mainly for Nginx, but the others should be similar) and it appears that the commands for optimizing laravel are run before Nginx / FPM is fully booted. Therefore, using Laravel should be OK here.

php artisan optimize

On the point above, I noticed that 50-laravel-automations.sh doesn't make use of artisan optimize. This command calls the other cache functions, as well as allows other libraries (such as Filament) to hook in any other caching / optimisation functions they use.

This would be especially useful here, as it would ensure that these are cached before Nginx / FPM is booted by default, making sure they'd be properly stored by OPcache.

Of course, it's simple enough to add your own script for it as an entrypoint, but it'd be nice to have it in by default.

Wordpress

You probably couldn't have this enabled by WordPress. Any time you install or update a plugin, the PHP changes. This would not be found by OPcache as it won't check for file changes. You could probably find something that invalidates those files on an update, or resets OPcache entirely, but that would be up to the user. Mostly marking this here for clarity.

Additional Environment Variables

Not the scope of this PR so I haven't included them, but exposing some other values to the environment might be helpful.

opcache.jit

Set the type of JIT compiler. Turned off by default due to next setting. There's quite a bit of discussion about PHPs new JIT compiler, but it can have significant performance benefits for repetitive functions / code.

opache.jit_buffer_size

Set the size in bytes of the JIT buffer cache. Disabled (0) by default. Can also be specified using shorthand notation. For example, 100M for 100 Megabytes.

opcache.force_restart_timeout

If OPcache hasn't been hit in a set amount of time, forcibly reset it. Default 180 seconds. This one might be useful, but documentation is a little scarce.

opcache.save_comments

Remove comments from OPcache to minify a bit further. Note: any code that depends on PHPDoc annotations can break from this.

@aSeriousDeveloper
Copy link
Author

I decided to edit the main PR (probably shoulda just made a comment...) of some extra notes regarding OPcache and laravel's caching functions. See the "Using PHP / Laravel Caching" and "php artisan optimize" sections, but for a summary.

  • I believe (correct my if I'm wrong) that Laravel's cached PHP like routes & views are compiled before Nginx / FPM boots, so they should still work with timestamp validation disabled
  • It would be nice if php artisan optimize was used in the Laravel Automations entrypoint instead of the individual cache commands, so that libraries & dependencies that hook into the optimize function are also called.

@jaydrogers
Copy link
Member

I believe (correct my if I'm wrong) that Laravel's cached PHP like routes & views are compiled before Nginx / FPM boots, so they should still work with timestamp validation disabled

Correct, these are called at container start with the Laravel Automations script.

It would be nice if php artisan optimize was used in the Laravel Automations entrypoint instead of the individual cache commands, so that libraries & dependencies that hook into the optimize function are also called.

I think a long time ago I had php artisan optimize, but I received feedback to separate it out so people could choose what they want to run explicitly. Can the commands hook into the separate commands that we already have?

@aSeriousDeveloper
Copy link
Author

aSeriousDeveloper commented Jan 15, 2025

I think a long time ago I had php artisan optimize, but I received feedback to separate it out so people could choose what they want to run explicitly. Can the commands hook into the separate commands that we already have?

Hmm, I don't think so sadly. An example for this is the dependency used by Filament, Blade Icons. It calls the optimizes function in BladeIconServiceProvider to hook the functions it uses to cache its icons.

if (method_exists($this, 'optimizes')) {
    $this->optimizes(
        'icons:cache',
        'icons:clear',
        'blade-icons'
    );
}

The current setup of the Laravel Automation script sadly wouldn't allow this to be called. However, one option here is maybe another environment variable? Similar to AUTORUN_LARAVEL_MIGRATION, we could have a variable of AUTORUN_LARAVEL_OPTIMIZE. If this is true, then use php artizan optimize instead of the individual cache functions.

the defaults it calls (and would now be skipped) are:

  • config:cache
  • event:cache
  • route:cache
  • view:cache

my guess would be wrapping those commands in the automation script into a function, and doing an if check for if AUTORUN_LARAVEL_OPTIMIZE is set to true. If so, simply run that. Otherwise, run the function to individually cache.

EDIT: decided to make a branch showcasing an example. I haven't run this locally, nor am I an expert at Shell scripts, but I believe this should work.

@jaydrogers
Copy link
Member

Thanks for the examples! Let me queue this up on my list for v3.6.

I want to make sure I take a look at this carefully and not screw things up for people who are using our defaults now:

image

I think I have it if you set AUTORUN_ENABLED = true, then it automatically enables everything else.

I'd love to support php artisan optimize too since today I learned other things hook into it. I'll keep you posted on this PR once I have a good moment to review. Thanks for your contribution! 👍

@aSeriousDeveloper
Copy link
Author

Thanks Jay! If you'd like, I can publish the branch with an option for optimize as a Draft PR? That way it'll at least be visible within the PRs section and can be discussed separately.

@jaydrogers
Copy link
Member

For sure, that would be great -- thanks!

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

Successfully merging this pull request may close these issues.

2 participants