-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Cache] Add (filesystem|phpfiles) cache (adapter|simple) prune method and prune command #23451
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
[Cache] Add (filesystem|phpfiles) cache (adapter|simple) prune method and prune command #23451
Conversation
41d099e
to
d414259
Compare
$i = 0; | ||
|
||
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::CHILD_FIRST, \RecursiveIteratorIterator::CATCH_GET_CHILD) as $file) { | ||
// remove empty directories |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Empty dirs shouldn't be removed: that creates a race condition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right; I didn't think about that. I've amended the RecursiveIteratorIterator
iterator to use the LEAVES_ONLY
mode constant (so only the files are recursed over without any dirs) and removed the inner directory logic entirely from the foreach.
* | ||
* @return int | ||
*/ | ||
public function prune($time = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why allow time configuration?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is useful in allowing cache entries to be purged starting at an arbitrary time. For example, the developer may need to clear all cache entries that would expire in the next hour, and short of fetching all and iterating over them, this isn't otherwise possible.
More simply, the argument provides fine-tuned control between a call to clear()
and prune()
by allowing one to purge anywhere in-between those two methods.
Do you see any value in this as explained?
} | ||
} | ||
|
||
return $i; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A boolean would be enough IMHO
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not extremely sided one way or another on the return type, but I will point out my motivation and you can instruct whether it adds any value: Generally, prune
will (or should) be run in cron or an otherwise scheduled command (outside of a normal request); as such, I figured it may be substantive to allow the user to log the behavior of purge
(specifically the number of items removed), which could provide insights into the size, growth, and general behavior of their cache items.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've gone ahead and changed the implementation to return bool
for now; please advise if you find value in the above and I can revert; for now, it's as requested!
e288617
to
d64598a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay doesn't exist, it's ok :) Maybe $pruned?
Also about the time arg, I personally don't see the need, so I'll let others confirm if this is really useful.
|
||
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD) as $file) { | ||
if ($time >= $file->getMTime()) { | ||
$okay = (@unlink($file) && !file_exists($file)) && $okay; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the first && should be an ||
933fb18
to
5700093
Compare
@nicolas-grekas Just noticed #21764 (comment) where you mentioned adding a |
@robfrawley yes please do, that would ease writting the command to prune (all prunable) pools. |
@nicolas-grekas What bundle should the command exist in? |
FrameworkBundle since that's where the other cache:pool commands are I'd say. |
Re: the command. Do you want a complex implementation with a compiler pass that assigns all specially tagged services to a "global cache pruner" abstraction that is then consumed by the command (like the "cache clearer" implementation) or do you want a simple command that just consumes a list of cache pools, grabs 'em from the container, ensures they implement the prunable interface, and calls |
The latter would just look like: namespace Symfony\Bundle\FrameworkBundle\Command;
use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Cache pool pruner command.
*/
final class CachePoolPruneCommand extends ContainerAwareCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('cache:pool:prune')
->addArgument('pools', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'A list of cache pools')
->setDescription('Prune cache pools')
->setHelp(<<<'EOF'
The <info>%command.name%</info> command prunes the given cache pools.
%command.full_name% <cache pool 1> [...<cache pool N>]
EOF
)
;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$container = $this->getContainer();
foreach ($input->getArgument('pools') as $id) {
if (!$container->has($id)) {
throw new \InvalidArgumentException(sprintf('"%s" cache pool not found in container.', $id));
}
if (!($pool = $container->get($id)) instanceof PruneableInterface) {
throw new \InvalidArgumentException(sprintf('"%s" is not a prunable cache pool.', $id));
}
$io->comment(sprintf('Pruning cache pool: <info>%s</info>', $id));
$pool->prune();
}
$io->success('Cache was successfully pruned.');
}
} |
See the cache pool clearer pass and command, it should be similar I guess |
@nicolas-grekas Is the most recent commit the direction you want this to go in? Just want to confirm before finishing up the implementation and ensuring all the required tests are present. |
instead of a tag, what about constructing a service locator + array or ids (the pool services), that would be given to the command? I'm not sure I see the need for an intermediary pruner service, WDYT? |
That sounds reasonable. The PR implementation went in this direction as you mentioned mirroring how the "cache clear" functionality was implemented. Having said that, I absolutely agree that a simpler approach would be preferable and that an intermediary pruner service is unnecessary. But, I'm not intimately familiar with service locators or their best practices. I get the basic concept, that you define a locator, specifying the services it should contain, and then use it like a Basically, how should the service locator be defined to allow dynamically passing all prunable pools to it? |
Do we need the command to be able to prune one specific pool given as argument? Or is pruning all prunable pools enough? I might think "all of them" is enough, because pruning does remove things that are stale only. |
247b365
to
5127fd2
Compare
Give the latest changes a quick once-over. Some items still need to be cleaned up, but I think this is the direction you described. As for passing pools as arguments and using an argument iterator, I don't think those two have to be mutually exclusive. See the command implementation that optionally allows passing pool names. Thoughts? I don't have a strong opinion if you think we should remove the option to do so. Also, right now the command filters the pools implementing the correct interface; I couldn't figure out how to filter pools during the compiler pass without reflection. If you think that's okay, the logic can be moved to the compiler pass by replacing /**
* @param ContainerBuilder $container
*
* @return IteratorArgument
*/
private function getCachePoolsIterator(ContainerBuilder $container)
{
$services = array_filter($this->getConcreteCachePoolServices($container), function ($name) use ($container) {
$reflection = new \ReflectionClass($container->getDefinition($name)->getClass());
return $reflection->implementsInterface(PruneableInterface::class);
});
return new IteratorArgument(array_combine($services, array_map(function ($id) {
return new Reference($id);
}, $services)));
} |
@@ -22,4 +22,52 @@ public function createSimpleCache($defaultLifetime = 0) | |||
{ | |||
return new FilesystemCache('', $defaultLifetime); | |||
} | |||
|
|||
public function testPrune() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this test fail without the other changes?
Looking at FilesystemCache::doContains
, I think the cache will already return false for expired entries, whether or not they have been pruned.
If this test passes regardless of whether pruning is working or not, then it's not actually testing the pruning functionality.
(same above)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a valid point; do you have any idea how to better test this functionality?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
create a pool with only expired items, then check that prune did empty the dir?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about this approach: https://github.com/symfony/symfony/pull/23451/files#diff-6388c149445d15d020386824fc3440c5R63
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; | ||
|
||
/** | ||
* Cache pool pruner command. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find "prune" a strange term for this. Is it in common use?
It seems to me that "garbage collection" or "gc" is a more commonly used term for deleting expired items.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are lots of relevant Google hits for "cache prune", so maybe this is just me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah; it's a common term. :-) The gc
term is common as well, but I think it is much less obvious as to its purpose (especially for those lacking a CS background).
faa5c1a
to
2377bb5
Compare
$doSet('qux', 'qux-val', new \DateInterval('PT80S')); | ||
|
||
$cache->prune(); | ||
$this->assertTrue($this->isPruned($cache, 'foo')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to be consistent with the naming, all true/false assertions should be swapped, and isPruned should return true when the key is pruned (it does the opposite right now isn't it?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right; that is due to how it used to be named isHit
--- I'll reverse the return now that the name has changed. ;-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have a preference between
return !$cache->getItem($name)->isHit() && !file_exists($getFileMethod->invoke($cache, $name))
and
return !($cache->getItem($name)->isHit() && file_exists($getFileMethod->invoke($cache, $name)))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's not the same :)
what about just return !file_exists($getFileMethod->invoke($cache, $name));
?
because basically that's what the "prune" method does: clean files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
S*it, you're right. :-( I must be getting tired... I think just checking the file exists is a good call.
$cache->set('qux', 'qux-val', new \DateInterval('PT80S')); | ||
|
||
$cache->prune(); | ||
$this->assertTrue($this->isPruned($cache, 'foo')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here, true/false should be swapped
$pruned = isset($expiresAt[0]) && @unlink($file) && !file_exists($file) && $pruned; | ||
} | ||
|
||
fclose($h); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that won't work on Windows, where you cannot unlink a file when all handlers to it are not closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
F*** Windows. :-) But really, I guess just add an else
branch?
if ($time >= (int) $expiresAt = fgets($h)) {
fclose($h);
$pruned = isset($expiresAt[0]) && @unlink($file) && !file_exists($file) && $pruned;
} else {
fclose($h);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
Additional changes pushed. If it's an okay time to squash the commits made for your comments into the original commit, let me know (don't want to do so before you can confirm the changes, as it would be harder to do so when squashed, obviously). Thanks for taking the time to so thoroughly review this PR! Not for this PR, but more generally out of interest (and possibly submission of additional PRs), you've mentioned that other PSR-(6|16) implementations may benefit from implementing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for the work!
the PdoAdapter first, then maybe ChainAdapter could propagate, and last to my mind comes the ApcuAdapter, looking for items that have an old |
…ne command - added `Symfony\Component\Cache\PruneableInterface` so PSR-6 or PSR-16 cache implementations can declare support for manual stale cache pruning - added FilesystemTrait::prune() and PhpFilesTrait::prune() implementations - now FilesystemAdapter, PhpFilesAdapter, FilesystemCache, and PhpFilesCache implement PruneableInterface and supports manual stale cache pruning - Added `cache:pool:prune` command via `Symfony\Bundle\FrameworkBundle\Command\CachePoolPruneCommand` to allow manual stale cache item pruning of supported PSR-6 and PSR-16 cache pool implementations - Added `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolPrunerPass` compiler pass to fetch all cache pools implementing `PruneableInterface` and pass them to the command as an `IteratorArgument` so these references are lazy loaded by the command - updated changelogs as appropriate
b2588e8
to
f0d0c5f
Compare
@nicolas-grekas I know #23603 is still fluid and unreviewed, but for the purpose of the documentation PR I am working on symfony/symfony-docs#8209, everything in this PR should be fairly solid at this point without any drastic changes, right? (Just want to confirm I'm not wasting effort documenting something that might need to change.) |
Thank you @robfrawley. |
…e) prune method and prune command (robfrawley) This PR was merged into the 3.4 branch. Discussion ---------- [Cache] Add (filesystem|phpfiles) cache (adapter|simple) prune method and prune command | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #21764, #21764 (comment) | License | MIT | Doc PR | symfony/symfony-docs#8209 As requested in #21764 (comment), this PR adds a `prune()` method to [`FilesystemTrait`](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Cache/Traits/FilesystemTrait.php). This placement seems reasonable as it exposes the method in [`FilesystemAdapter`](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php) and [`FilesystemCache`](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Cache/Simple/FilesystemCache.php). The return value is a `bool` representing either a partial or complete failure (when `false`) *or* complete success (when `true`). Once the API for the `prune` method is confirmed, I'll introduce a documentation PR, as well. --- *Stale-detection implementation:* The file modification time is used to determine if a cache item should be pruned. This seems reasonable, given the use of [`touch` in the common trait](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php#L90). Interestingly, though, the [`doFetch` method](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Cache/Traits/FilesystemTrait.php#L38) uses the timestamp saved at the top of the file itself to determine the stale state. Should this latter implementation be used for `prune` as well (or is the current one ok), for example: ```php foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD) as $file) { if ($h = @fopen($file, 'rb')) { if ($time >= (int) $expiresAt = fgets($h)) { fclose($h); if (isset($expiresAt[0])) { $okay = (@Unlink($file) && !file_exists($file)) && $okay; } } } } ``` Commits ------- f0d0c5f add (filesystem|phpfiles) cache (adapter|simple) prune method and prune command
…ethod (robfrawley) This PR was merged into the 3.4 branch. Discussion ---------- [Cache] Add (pdo|chain) cache (adapter|simple) prune method | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | | License | MIT | Doc PR | symfony/symfony-docs#8209 This is additional work toward making more cache implementations use the `PruneableInterface` contract. Specifically, this pull request focuses on `(Pdo|Chain)(Cache|Adapter)`, as suggested by @nicolas-grekas in #23451 (comment). Commits ------- b20a237 add (pdo|chain) cache (adapter|simple) prune method
…ethod (robfrawley) This PR was merged into the 3.4 branch. Discussion ---------- [Cache] Add (pdo|chain) cache (adapter|simple) prune method | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | | License | MIT | Doc PR | symfony/symfony-docs#8209 This is additional work toward making more cache implementations use the `PruneableInterface` contract. Specifically, this pull request focuses on `(Pdo|Chain)(Cache|Adapter)`, as suggested by @nicolas-grekas in symfony/symfony#23451 (comment). Commits ------- b20a237 add (pdo|chain) cache (adapter|simple) prune method
@nicolas-grekas there's some design issue here when using the $cache = new TagAwareAdapter(new FilesystemAdapter());
// Does $cache implement Pruneable now?
I figured the easiest way would be to have a class PruneableTagAwareAdapter extends TagAwareAdapter implements PruneableInterface
{
/**
* @return bool
*/
public function prune()
{
return $this->itemsAdapter->prune();
}
} However, the |
@Toflar Looks like your concern is reasonable, but it is generally better to open a new issue/pr than to comment on a closed one, as it removes visibility of the issue from anyone not directly involved in the existing pr, and not everyone actually follows-up on comments to closed issues/prs. :-) That said, why not just make the adapter prune-aware instead of adding complexity with an additional interface: class TagAwareAdapter implements PruneableInterface
{
/* ... */
/**
* @return bool
*/
public function prune()
{
if ($this->itemsAdapter instanceof PruneableInterface) {
return $this->itemsAdapter->prune();
}
return false;
}
} |
Yeah, that's what I meant with my last sentence :) Will create a PR directly then :)
|
…lar) This PR was submitted for the master branch but it was merged into the 3.4 branch instead (closes #24075). Discussion ---------- Implemented PruneableInterface on TagAwareAdapter | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | See comment on #23451 | License | MIT | Doc PR | Added a comment on symfony/symfony-docs#8209 The `ChainAdapter` already delegates the `prune()` calls. The `TagAwareAdapter` is a wrapping adapter too and should delegate the `prune()` calls just the same. Commits ------- b635b62 Implemented PruneableInterface on TagAwareAdapter
private $cacheCommandServiceId; | ||
private $cachePoolTag; | ||
|
||
public function __construct($cacheCommandServiceId = 'cache.command.pool_pruner', $cachePoolTag = 'cache.pool') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty sure this is broken right now as cache.command.pool_pruner
doesn't exist.
…ommand service id (kbond) This PR was merged into the 3.4 branch. Discussion ---------- [FrameworkBundle] fix CachePoolPrunerPass to use correct command service id | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #23451 (comment) | License | MIT | Doc PR | n/a #23624 broke #23451. Commits ------- 14c62da fix CachePoolPrunerPass to use correct command service id
…dapter|simple) prune method and prune command (robfrawley, javiereguiluz) This PR was merged into the 3.4 branch. Discussion ---------- [Cache] Document (filesystem|phpfiles|pdo|chain) cache (adapter|simple) prune method and prune command This PR documents the changes introduced in symfony/symfony#23451 and symfony/symfony#23603: - The addition of [`PruneableInterface`](https://github.com/symfony/cache/blob/master/PruneableInterface.php) - The variouse, concrete [`PruneableInterface::prune()`](https://github.com/symfony/cache/blob/master/PruneableInterface.php#L22) implementations in [`ChainAdapter`](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Cache/Adapter/ChainAdapter.php), [`FilesystemAdapter`](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php), [`PdoAdapter`](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Cache/Adapter/PdoAdapter.php), [`PhpFilesAdapter`](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php),[ `ChainCache`](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Cache/Simple/ChainCache.php), [`FilesystemCache`](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Cache/Simple/FilesystemCache.php), [`PdoCache`](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Cache/Simple/PdoCache.php), [`PhpFilesCache`](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Cache/Simple/PhpFilesCache.php), and [`TagAwareAdapter`](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php). - The addition of [`CachePoolPruneCommand`](https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolPruneCommand.php), invokable via `cache:pool:prune`. This command iterates over all services tagged `cache.pool` and calls the [`PruneableInterface::prune()`](https://github.com/symfony/cache/blob/master/PruneableInterface.php#L22) method on those that implement [`PruneableInterface`](https://github.com/symfony/cache/blob/master/PruneableInterface.php) Additionally, some language changes and cleanup has been implemented for the various cache docs. Of note are the following changes: - Additional `note` blocks for all caches that implement [`PruneableInterface`](https://github.com/symfony/cache/blob/master/PruneableInterface.php) - Addition of `prune()` description and usage example on the chain adapter - Addition of a `tip` about achieving better performance with the filesystem adapter by utilizing `tmpfs` or another ram disk solution - Fix for an incorrect `use` statement in the PHP array cache adapter code example - Addition of documentation page for PHP files adapter - Addition of a "pruning cache items" explanation and example on the main cache pools page Commits ------- 54a1e62 Typo 1322ffd Typos and wrapped long lines 5bea1e6 Fixed a typo eb70a36 Wrapped long lines ea29252 document cache pruning functionality - note the addition of PruneableInterface - note the variouse, concrete PruneableInterface::prune() implementations in ChainAdapter, FilesystemAdapter, PdoAdapter, PhpFilesAdapter, ChainCache, FilesystemCache, PdoCache, PhpFilesCache, and TagAwareAdapter. - note the addition of CachePoolPruneCommand, invokable via cache:pool:prune. This command iterates over all services tagged cache.pool and calls the PruneableInterface::prune() method on those that implement PruneableInterface - rewording of some adapter text for clarity - additional note blocks for all caches that implement PruneableInterface - addition of prune() description and usage example on the chain adapter - addition of a tip about achieving better performance with the filesystem adapter by utilizing tmpfs or another ram disk solution - fix for an incorrect use statement in the PHP array cache adapter code example - addition of documentation page for PHP files adapter - addition of a "pruning cache items" explanation and example on the main cache pools page
As requested in #21764 (comment), this PR adds a
prune()
method toFilesystemTrait
. This placement seems reasonable as it exposes the method inFilesystemAdapter
andFilesystemCache
.The return value is a
bool
representing either a partial or complete failure (whenfalse
) or complete success (whentrue
).Once the API for the
prune
method is confirmed, I'll introduce a documentation PR, as well.Stale-detection implementation: The file modification time is used to determine if a cache item should be pruned. This seems reasonable, given the use of
touch
in the common trait. Interestingly, though, thedoFetch
method uses the timestamp saved at the top of the file itself to determine the stale state. Should this latter implementation be used forprune
as well (or is the current one ok), for example: