php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #77478 opcache_reset() does not reset an opcache.file_cache-based cache
Submitted: 2019-01-17 12:28 UTC Modified: -
Votes:10
Avg. Score:4.1 ± 1.3
Reproduced:8 of 9 (88.9%)
Same Version:2 (25.0%)
Same OS:7 (87.5%)
From: anders dot henke at 1und1 dot de Assigned:
Status: Open Package: opcache
PHP Version: 7.3.1 OS: Linux
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: anders dot henke at 1und1 dot de
New email:
PHP Version: OS:

 

 [2019-01-17 12:28 UTC] anders dot henke at 1und1 dot de
Description:
------------
From PHP 7.0 on, PHP's opcache includes a file-cache as an optional overflow mechanism and optionally as the only kind of opcache storage (opcache.file_cache_only=1). The later is interesting in CGI-mode, where shared memory does not survive a single PHP run.

PHP's opcache does provide a function opcache_reset() to "reset the entire opcode cache. After calling opcache_reset(), all scripts will be reloaded and reparsed the next time they are hit.".

This function may be called explicitely (e.g. during software deployment) to clear any caches and remove accumulated stale cache objects who are no longer being accessed. Whenever a file-based opcache storage is being added to Opcache, opcache_reset() does only reset a shared memory-based opcache, but does not reset any file-based opcache storage.

In a performance-optimized setup, a user may use shared-memory-based opcache with file-based opcache storage as a fallback, disable automatic revalidation and manually perform opcache_reset() during the deployment process. In such a situation, opcache_reset() returns true and restarts the shared memory-based storage, but does not remove any objects from the file-based opcache storage. This possibly might result in running code from outdated cache objects.

Test script:
---------------
php.ini:
---cut
zend_extension=opcache.so;
opcache.enable=1;
opcache.revalidate_freq=180;
opcache.validate_timestamps=2;
opcache.file_cache=/var/tmp/opcache;
opcache.file_cache_only=1;
---cut

clear.php:

---cut
<pre>
<?php
echo "**Before Clear Opcache:\n";
system('ls -lRt ' . ini_get('opcache.file_cache'));

echo "\n\n**Clear Opcache: ";
var_dump(opcache_reset());
echo "\n";

echo "**After Clear Opcache:\n";
system('ls -lRt ' . ini_get('opcache.file_cache'));

?>
</pre>
---cut

Expected result:
----------------
http://php.net/manual/en/function.opcache-reset.php states "This function resets the entire opcode cache. After calling opcache_reset(), all scripts will be reloaded and reparsed the next time they are hit.

According to my reading, "reset the entire opcache" does also include the file-based opcache.

In my test script above, the actual content "before" and "after" opcache_reset() stays the same.

Actual result:
--------------
opcache_reset() does not affect a file-based opcache at all and only restarts the shared-memory-based opcache.

In a file_cache_only-configuration, opcache_reset() does not reset the file-based opcache, but returns "false", which should only happen when opcache is disabled.
According to phpinfo(), opcache is enabled and running.

In a overflow/fallback configuration, opcache_reset() does return true, but will only restart the shared memory storage and not reset any file-based opcache storage. Depending on the overall configuration, this might result in running cached but outdated code. Such an issue has also been reported in bug #75670.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-01-25 20:58 UTC] post at minhost dot no
To me this is not a bug. We depend on opcache.file_cache as a second level fallback cache used when PHP-FPM is restarted, so that even when opcache in memory is empty, the first hit will be from file cache.

If opcache_reset is changed so that the files in opcache.file_cache is emptied every time PHP-FPM is restarted, that would be very bad, as it would defeat the purpose of having opcache.file_cache as second level fallback cache.

Also you can set opcache.revalidate_freq=0 (like we have done), so that the timestamp is checked for every updates on every request, that way you will never get code from outdated cache objects.

Also, if you update PHP every month with a new PHP version, then the entire directory for file_cache get a new name/number, and you simply delete the old directory after each PHP upgrade.

I ask that the current behavior is not changed, as we very much depend on having opcache.file_cache as a second level fallback cache, so that first request when opcache in memory is empty, is from the opcache.file_cache
 [2019-01-28 11:09 UTC] anders dot henke at 1und1 dot de
The documentation for opcache_reset() clearly states: "reset the entire opcode cache. After calling opcache_reset(), all scripts will be reloaded and reparsed the next time they are hit."

In contrast to the docs, opcache_reset() does not clear the entire opcode cache, and post at minhost dot no is using this bug as a feature.

Probably either one of us does need a more adjustable opcache_reset() mechanism (flush everything, flush only memory, flush only outdated hash directrories from file cache, flush entire file cache) or an option to have opcache_reset() only flush specific parts of opcache (with a configurable default).

> Also you can set opcache.revalidate_freq=0 (like we have done), so that the timestamp is checked for every updates on every request, that way you will never get code from outdated cache objects.

Revalidation does only check for changed files, but is unable to remove objects from cache whose source has been moved or removed - those files stay in the file cache. And with every PHP version, a new hash directory is created, resulting in a new copy of the cache. Unless the file cache is manually purged, a lot of cruft does accumulate in the file cache. A cache should be managed automatically and not rely on someone manually removing files.

> Also, if you update PHP every month with a new PHP version, then the entire directory for file_cache get a new name/number, and you simply delete the old directory after each PHP upgrade.

If the "freshness" of a hash directory were known, opcache_reset() invoked after an update could also remove the file cache for the outdated hash directories.

Manually removing file caches is a workaround I'm certainly not happy with.
 [2019-01-28 11:27 UTC] spam2 at rhsoft dot net
> To me this is not a bug. 

it is

> We depend on opcache.file_cache as a second level fallback 
> cache used when PHP-FPM is restarted

irrelevant! when you say "reset cache" you mean exactly that
 [2019-08-17 01:23 UTC] rolmos at endertech dot com
This is related to an open report from 2016: https://bugs.php.net/bug.php?id=71749

Also experiencing the same issue. What's the official method to clear the memory & file cache so that changes are detected by opcache, without a race condition of manually deleting cache files?
 
PHP Copyright © 2001-2020 The PHP Group
All rights reserved.
Last updated: Wed Oct 28 03:01:23 2020 UTC