Tom Cannaerts

PHP Opcache and symlinks

If you’re using automated deployments on a regular base, chances are that you are using symlinks to limit the amount of downtime to a minimum. This is a proven approach that has worked well for many years.

website
 current -> releases/release-12
 releases/release-11
 releases/release-12

When time has come to deploy a new release, the release is prepared in releases/release-13. Once all preparations are done, the current symlink is updated to point to release-13 and the new version of the site is online instantaniously.

APC

When it comes to opcode caching, the old APC would simply monitor the file timestamp for changes. It did not resolve the symlink and would check the timestamp through the symlink. When the symlink was updated, APC would check the timestamp of the file in the new location.

OPCache

OPCache on the other hand is aware of the symlink and will resolve the symlink to the actual location of the file. When the symlink changes to a new version, OPCache will still be checking the file in the old release folder. As that file didn’t change, the cache will not be invalidated and the website would still be serving of the old release.

To overcome this problem, you will need to clear OPCache. This can be done in serveral ways, depending on how you are running your PHP process.

opcache_reset();

One of the easiest ways for developers to reset the opcache, is by calling the PHP opcache_reset() function. A simple file with nothing else in it should do the trick in most cases:

<?php opcache_reset();

The key here is to run this function in the same way as the opcache you are trying to reset. As the opcache is bound to the running process, calling opcache_reset from the CLI will not clear the OPCache from your webserver. This means that you probably need to use curl or wget in your deployment process to clear the cache. Note that when running your PHP through (fast)cgi, this might not be a good option.

If opcache_reset would not be an option, you still have alternative options to clear the OPCache

Apache’s mod_php

If you’re running PHP through Apache’s mod_php, you need to reload or restart Apache. This will stop the old process and spawn a new one, resulting in an empty OPCache.

service apache2 reload
-or-
service apache2 restart

FastCGI

If you’re using FastCGI to run your PHP, your PHP processes will probably automatically be recycled every X requests to prevent memory leaks. Depending on how high your X is and how much traffic the site gets, this could take anywhere from a few seconds to infinity before the process is recycled.

Also note that each FastCGI worker process will have its own OPCache. Using the opcache_reset() function will not clear the OPCache of all processes, just the process that handles the request. To force reload the processes, you typically need to restart your webserver or kill all the worker processes.

PHP-FPM

The nice part about PHP-FPM is that OPCache is stored in the master process, not in the workers. If one worker clears OPCache using opcache_reset(), it is cleared for all workers. The drawback is that if you are running a single master process with multiple pools, the opcache of all pools would be cleared, not just the pool that made the call. To overcome this, you can run each pool off its own master process.

Another way to clear the cache, is to simply restart the master process. This will clear the OPCache of all pools running off this master process.

Alternatively, you can also look into tools like cachetool that can directly connect to the PHP-FPM socket to clear the cache.

Conclusion

When using symlinks in your deployment setup, you will need to trigger a clear of the OPCache after you have updated the symlink. The options vary depending on how you are running your PHP. If possible, try choosing an option that does not involve hard restarting any services, as these might result in temporary failures of your website.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.