Symfony 3.1 缓存组件中的缓存失效

Cache invalidation in Symfony 3.1 cache component

我使用 Symfony 3.1 的 cache component 为我的实体存储一些自定义元数据。我想在与这些元数据关联的任何文件发生更改后立即使其失效。

我没有找到一种方法来告诉 Symfony 或特别是缓存组件观察特定文件集的变化,我错过了什么?

在我用来创建缓存项池的代码下方:

<?php
class MetadataCacheFactory implements MetadataCacheFactoryInterface
{
    const CACHE_NAMESPACE = 'my_namespace';

    /** @var string */
    protected $cacheDir;

    public function __construct(KernelInterface $kernel)
    {
        $this->cacheDir = $kernel->getCacheDir();
    }

    /**
     * {@inheritdoc}
     */
    public function create(): CacheItemPoolInterface
    {
        return AbstractAdapter::createSystemCache(self::CACHE_NAMESPACE, 0, null, $this->cacheDir);
    }
}

以及使用它的代码:

<?php
class ExampleMetadataFactory implements MetadataFactoryInterface
{
    const CACHE_KEY = 'example_metadata';

    [...]

    /** @var ExampleMetadata */
    protected $metadata;

    public function __construct(MetadataCacheFactoryInterface $cacheFactory)
    {
        $this->cache = $cacheFactory->create();
        $this->metadata = null;
    }

    /**
     * {@inheritdoc}
     */
    public function create(): ExampleMetadata
    {
        if ($this->metadata !== null) {
            return $this->metadata;
        }
        try {
            $cacheItem = $this->cache->getItem(md5(self::CACHE_KEY));
            if ($cacheItem->isHit()) {
                return $cacheItem->get();
            }
        } catch (CacheException $e) {
            // Ignore
        }
        $this->metadata = $this->createMetadata();
        if (!isset($cacheItem)) {
            return $this->metadata;
        }
        $cacheItem->set($this->metadata);
        $this->cache->save($cacheItem);
        return $this->metadata;
    }
}

当我在运行时查看代码时,AbstractAdapter 选择给我一个 PhpFilesAdapter(我想在开发中足够公平)。

但是当我查看这个适配器的代码时,我发现:

<?php
protected function doFetch(array $ids) {
    [...]

    foreach ($ids as $id) {
        try {
            $file = $this->getFile($id);
            list($expiresAt, $values[$id]) = include $file;
            if ($now >= $expiresAt) {
                unset($values[$id]);
            }
        } catch (\Exception $e) {
            continue;
        }
    }
}

所以除了过期日期之外,根本没有检查过期的逻辑。

您知道在文件更改时使缓存无效的方法吗(当然是在开发环境中)?还是我自己实现..?

感谢您的帮助。

我找到了解决方案:ConfigCache。你可以给它一个资源数组来观察,它会在每次读取缓存时检查它们的最后修改时间。

上面的例子变成这样:

<?php
class MetadataCacheFactory implements MetadataCacheFactoryInterface
{
    const CACHE_NAMESPACE = 'my_namespace';

    /** @var \Symfony\Component\HttpKernel\KernelInterface */
    protected $kernel;

    public function __construct(KernelInterface $kernel)
    {
        $this->kernel = $kernel;
    }

    /**
     * {@inheritdoc}
     */
    public function create(): ConfigCache
    {
        return new ConfigCache($this->kernel->getCacheDir().'/'.self::CACHE_NAMESPACE, $this->kernel->isDebug());
    }
}

以及使用它的代码:

<?php
class ExampleMetadataFactory implements MetadataFactoryInterface
{
    const CACHE_KEY = 'example_metadata';

    [...]

    /** @var ExampleMetadata */
    protected $metadata;

    public function __construct(MetadataCacheFactoryInterface $cacheFactory)
    {
        $this->cache = $cacheFactory->create();
        $this->metadata = null;
    }

    /**
     * {@inheritdoc}
     */
    public function create(): ExampleMetadata
    {
        if ($this->metadata !== null) {
            return $this->metadata;
        }
        try {
            if ($this->cache->isFresh()) {
                $this->metadata = unserialize(file_get_contents($this->cache->getPath()));
                return $this->metadata;
            }
        } catch (CacheException $e) {
            // Ignore
        }
        $resources = [];
        $this->metadata = $this->createMetadata($resources);
        $this->cache->write(serialize($this->metadata), $resources);
        return $this->metadata;
    }

    /**
     * Creates the metadata.
     *
     * @param array $resources
     *
     * @return ExampleMetadata
     */
    protected function createMetadata(&$resources): ExampleMetadata
    {
        $metadata = new ExampleMetadata();
        $resourcesCollection = $this->resourcesCollectionFactory->create();
        foreach ($resourcesCollection as $resourceClass => $resourcePath) {
            // The important part, it's an instance of \Symfony\Component\Config\Resource\FileResource.
            $resources[] = new FileResource($resourcePath);
            $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
            $metadata->registerResourceMetadata($resourceMetadata);
        }
        return $metadata;
    }
}

希望能帮到别人。