Skip to content

[Cache] Cannot warm or clear APCu cache from CLI with apc.enable_cli set to false #53962

@GuySartorelli

Description

@GuySartorelli

Symfony version(s) affected

>= 3.4

Description

If apc.enable_cli is not set to true, the ChainAdapter will skip the ApcuAdapter even if ApcuAdapter::isSupported() returns true.
This change was introduced in #36555

This means that CLI can't be used to warm APCu cache even when that is actually supported.

How to reproduce

<?php

use Symfony\Component\Cache\Adapter\ApcuAdapter;
use Symfony\Component\Cache\Adapter\ChainAdapter;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;

require_once __DIR__ . '/vendor/autoload.php';

$cacheNamespace = 'MyCacheExample';
$cacheLifetime = 300;
$isCli = 'cli' === \PHP_SAPI && !ini_get('apc.enable_cli');

$adapters = [
    new FilesystemAdapter($cacheNamespace, $cacheLifetime, __DIR__ . '/' . $cacheNamespace)
];

if (ApcuAdapter::isSupported()) {
    $apcuAdapter = new ApcuAdapter($cacheNamespace, $cacheLifetime);
    // Note the order doesn't REALLY matter for this demonstration, but we should still ideally have the cache we *know* we can read from first.
    if ($isCli) {
        // Add it second so we're pulling from the filesystem, since APCu isn't available for reading
        $adapters[] = $apcuAdapter;
    } else {
        // Add it first so we're pulling from memory where possible
        array_unshift($adapters, $apcuAdapter);
    }
}

$cache = new ChainAdapter($adapters, $cacheLifetime);

if ($isCli) {
    echo "CLI" . PHP_EOL;
    $cache->clear();
} else {
    echo "Not CLI" . PHP_EOL;
}
$cache->get('my-cache-item', function () use ($isCli) {
    $value =  $isCli ? 'CLI value' : 'not-CLI value';
    return $value . time();
});
$cache->commit();

echo $cache->getItem('my-cache-item')->get();

I think the order you need to run this is:

  1. from a non-CLI context (or with apc.enable_cli set to true)
  2. from a CLI context with apc.enable_cli set to false (or maybe not set at all?)
  3. from a non-CLI context (or with apc.enable_cli set to true) again.

The first run will set the cache in APCu.
The second run will clear the cache - but since it's skipping APCu it only clears the (already empty) filesystem cache. It then sets a value into the filesystem cache.
The third run will get a cache hit from APCu - it completely ignores the fact that the CLI run was meant to clear it and set a new value.

This should get you into a state where the CLI output is always different from the non-CLI output (at least until the APCu cache times out, at which point you might get lucky and they'll sync up)

Possible Solution

Ideally, that continue in ChainAdapter's constructor would just be outright removed.

If we want to be really careful, then if apc.enable_cli is not set to true the chain adapter could skip it anywhere that reads the adapter's cache, but still use it to set cache so it can warm that cache up and clear it.
I don't fully understand how the internals of this component work so I can't speak to the feasibility of that.

Additional Context

Note that in #25080 the suggestion to check apc.enable_cli to determine whether APCu was rejected specifically because it would prevent this use case - so obviously this at least used to be an intended use case.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy