Skip to content

Cache

The framework provides a Redis-backed cache system organised into named pools. Each pool is an isolated namespace in Redis, independently enabled or disabled, and clearable individually or all at once.

All pool identifiers are cases on Amarant\Framework\Enum\CacheTypeEnum.


Built-in pools

CacheTypeEnum case Pool ID (string value) Purpose
TYPE_DEFAULT cache.pool.default General-purpose pool. Use when no more specific pool applies.
TYPE_VIEW_LAYOUT cache.pool.view.layout Compiled layout XML.
TYPE_VIEW_PAGE cache.pool.view.page Full rendered pages. Cleared when data models that affect page content change.
TYPE_VIEW_BLOCK cache.pool.view.block Rendered block fragments.
TYPE_HTTP cache.pool.http HTTP-level response cache (application-side reverse proxy mode).
TYPE_REVERSE_PROXY cache.pool.reverse.proxy Tag-based invalidation bridge to an external reverse proxy (e.g. Varnish).
TYPE_DATA_MODEL_METADATA cache.pool.datamodel.metadata Database schema metadata (column types, primary keys).
TYPE_DATA_MODEL_DATA cache.pool.datamodel.data Query result cache for data models that opt in via isCacheable().
TYPE_ATTRIBUTE_MODEL cache.pool.attribute.model Attribute model definitions.
TYPE_CONFIG cache.pool.config Application configuration values.
TYPE_API_RATE_LIMITER cache.pool.api.rate.limiter API rate limit counters.
TYPE_COLLECTION_VERSION cache.pool.collection.version Version tokens for collection-level cache invalidation.

Using a pool

Inject a specific pool directly using DiEnum::getIdInjection(CacheTypeEnum::TYPE_*).

Vendor/ModuleName/Configuration/Di.php
1
2
3
4
5
6
7
8
9
use Amarant\Framework\Contract\Di\ConfigurableContainerInterface;

$container->bind(
    MyService::class,
    fn (ConfigurableContainerInterface $container) => $container->getBindBuilder()
        ->type(MyService::class)
        ->argument('pool', DiEnum::getIdInjection(CacheTypeEnum::TYPE_DEFAULT))
        ->build()
);

The injected type is Amarant\Framework\Contract\Cache\CacheItemPoolInterface (or AdvancedCacheItemPoolInterface if tag support is needed).


Cache item API

All pool operations return or accept Amarant\Framework\Cache\CacheItem.

Method Description
isHit(): bool true if the item exists in cache.
get(): mixed The cached value, or null on a miss.
set(mixed $value): static Set the value to store.
expiresAfter(int\|DateInterval\|null $time): static Set TTL in seconds (or as DateInterval). null means no expiry.
expiresAt(?DateTimeInterface $expiration): static Set absolute expiry time.
setTags(?array $tags): void Attach cache tags for group invalidation.
getTags(): ?array Get the attached tags.
getTtl(): ?int Get the TTL in seconds.
1
2
3
4
5
$item = $pool->getItem('product_123');
$item->set($product);
$item->expiresAfter(600);
$item->setTags(['product', 'product_123']);
$pool->save($item);

Tag-based invalidation

AdvancedCacheItemPoolInterface (implemented by RedisPool) adds clearTags(array $tags). All items saved with a matching tag are deleted atomically.

$pool->clearTags(['product', 'product_123']);

Tags are stored as Redis sets. Each save() call registers the item key under every tag it carries.


Deferred saves

saveDeferred queues an item for writing. Call commit() to flush all queued items in a pipeline.

$pool->saveDeferred($item);
// ... queue more items ...
$pool->commit();

Disabling pools via environment

Individual cache types can be disabled without code changes by setting environment variables. Disabled pools return cache misses on every read, but writes and invalidations still execute (so cross-process clearing still works).

Environment variable Scope Format
AMA_CACHE_DISABLE All areas Comma-separated pool IDs, or all
AMA_CACHE_DISABLE_FRONTEND Frontend area only Same
AMA_CACHE_DISABLE_BACKEND Backend area only Same
AMA_CACHE_DISABLE_CLI CLI only Same
# Disable all caches in every area
AMA_CACHE_DISABLE=all

# Disable only config and data-model caches in the frontend
AMA_CACHE_DISABLE_FRONTEND=cache.pool.config,cache.pool.datamodel.data

The constants for the environment key names are cases on CacheTypeEnum: DISABLED_CACHES_ENV_KEY, DISABLED_CACHES_FRONTEND_ENV_KEY, DISABLED_CACHES_BACKEND_ENV_KEY, DISABLED_CACHES_CLI_ENV_KEY.


Adding a custom pool

Bind the pool as a named child of RedisPool, tag it with DiEnum::CACHE_POOL, and register it in PoolList.

Vendor/ModuleName/Configuration/Di.php
use Amarant\Framework\Cache\Pool\PoolList;
use Amarant\Framework\Cache\Pool\RedisPool;
use Amarant\Framework\Contract\Di\ConfigurableContainerInterface;
use Amarant\Framework\Enum\DiEnum;
use Vendor\ModuleName\Enum\CacheTypeEnum as ModuleCacheTypeEnum;

$container
    ->bind(
        ModuleCacheTypeEnum::TYPE_MY_CACHE->value,           // (1)
        fn (ConfigurableContainerInterface $container) => $container->getBindBuilder()
            ->parent(RedisPool::class)                       // (2)
            ->argument('namespace', ModuleCacheTypeEnum::TYPE_MY_CACHE->value)
            ->build()
    )
    ->tag(
        [ModuleCacheTypeEnum::TYPE_MY_CACHE->value],
        [DiEnum::CACHE_POOL]                                 // (3)
    )
    ->extend(
        PoolList::class,
        function (BindInterface $bind) {
            $bind->addValueToArgument(
                'pools',
                DiEnum::getIdInjection(ModuleCacheTypeEnum::TYPE_MY_CACHE),
                ModuleCacheTypeEnum::TYPE_MY_CACHE->value    // (4)
            );
            return $bind;
        }
    );
  1. Use a BackedEnum value from your own module's cache type enum as the binding identifier.
  2. Inherit all Redis wiring from the RedisPool template binding.
  3. Tag the pool with DiEnum::CACHE_POOL so it appears in pool lists and is clearable via the admin.
  4. Register it in PoolList under its string name so it can be fetched by name and cleared via cache:clear.

Define the enum in your module, not in the framework:

Vendor/ModuleName/Enum/CacheTypeEnum.php
enum CacheTypeEnum: string
{
    case TYPE_MY_CACHE = 'cache.pool.vendor.module.my_cache';
}

Collection version cache

Amarant\Framework\Contract\Cache\CollectionVersionCacheInterface (implemented by Amarant\Framework\Cache\CollectionVersionCache) provides a lightweight version-token mechanism for invalidating in-memory caches that hold collections of data.

1
2
3
4
5
6
7
8
9
// At startup or at the top of a message handler — check whether the in-memory collection is stale
$storedVersion = $this->collectionVersionCache->getVersion('my_collection');
if ($storedVersion !== $this->localVersion) {
    $this->localVersion = $storedVersion;
    $this->rebuildCollection();
}

// When the underlying data changes — bump the version
$this->collectionVersionCache->bump('my_collection');

bump() writes microtime(true) as the new version string for the given collection key. Long-running worker processes (which have their own in-memory state) read getVersion() at the start of each message to detect staleness, then rebuild their local state and store the new version.

The collection version cache uses the CacheTypeEnum::TYPE_COLLECTION_VERSION pool, bound to CollectionVersionCacheInterface in DI.


HTTP response caching

HTTP responses can be cached at the application layer. Controllers and event subscribers communicate caching intent via request context metadata keys defined on RequestContextInterface.

Metadata key constant Description
META_RESPONSE_TTL Integer TTL in seconds for the full HTTP response.
META_RESPONSE_LAYOUT_RENDERED_PAGE_TTL TTL used specifically for layout-rendered page responses. Takes precedence over META_RESPONSE_TTL for page responses.
META_RESPONSE_TAGS Array of cache tags to associate with this response (used for tag-based purging).
META_CACHED Set by the cache subscriber when a response is served from cache, so other subscribers can detect a cache hit.
1
2
3
$requestContext->setMeta(RequestContextInterface::META_RESPONSE_TTL, 300);
$requestContext->appendMeta(RequestContextInterface::META_RESPONSE_TAGS, 'product');
$requestContext->appendMeta(RequestContextInterface::META_RESPONSE_TAGS, 'product_123');

These are read by Amarant\Framework\EventSubscriber\Request\CacheEventSubscriber, which stores or serves the response using the configured reverse proxy pool.


HTTP cache context (Vary)

The application generates a cache vary key that distinguishes cached responses for different visitor contexts (e.g. different stores, customer groups). This key is computed by Amarant\Framework\Cache\Http\Context and sent to the client as the X-Amarant-Vary cookie (expires in one year).

The vary key is a SHA-1 hash of the sorted, JSON-encoded context data. The full cache key for a stored response combines the request URL (scheme + host + path + filtered query parameters) with the vary hash, then SHA-256 hashes the result.

Cache context processors

Context data is populated by cache context processors — classes implementing CacheContextProcessorInterface, tagged with DiEnum::CACHE_CONTEXT_PROCESSOR. CacheContextResolver runs all tagged processors in order and merges their output into the context.

The framework ships ScopeProcessor, which adds data_scope and data_scope_code to the context (so different stores get distinct cached responses). Modules add their own processors to vary by additional dimensions (e.g. customer group, logged-in state).

Vendor/ModuleName/Configuration/Di.php
$container->tag(
    [MyContextProcessor::class],
    [DiEnum::CACHE_CONTEXT_PROCESSOR]
);

Reverse proxy integration

Two reverse proxy implementations are provided, selected via configuration.

Application cache (http_application)

Amarant\Framework\Cache\Http\ReverseProxy\HttpApplicationProxy stores full HTTP response bodies in the CacheTypeEnum::TYPE_HTTP Redis pool. This is an in-process implementation — no external infrastructure is required.

  • Cache key: SHA-256 of scheme + host + path + filtered_query + vary_context.
  • Ban: calls clearTags() on the pool for the specified tags.
  • Purge: calls clear() on the pool to wipe all stored responses.

Varnish

Amarant\Framework\Cache\Http\ReverseProxy\Varnish communicates with one or more Varnish servers via HTTP BAN and PURGE requests.

  • Ban — sends an HTTP BAN request with an X-Amarant-Tags-Pattern header. Varnish must be configured to honour this header and invalidate matching cached objects. Tags are automatically chunked to stay within HTTP header size limits.
  • Purge — sends an HTTP PURGE request to each configured channel's host to flush all cached content.

Configuration

ConfigEnum path Description
CACHING_REVERSE_PROXY_ENABLED Master switch.
CACHING_REVERSE_PROXY_TYPE varnish or http_application.
CACHING_REVERSE_PROXY_VARNISH_PURGE_IPS Comma-separated IPs allowed to send purge requests to Varnish.

The active implementation is resolved by CachingReverseProxyPool::getConfigured(), which matches the configured type string against all managers tagged with DiEnum::CACHE_HTTP_CACHING_REVERSE_PROXY_MANAGEMENT.


CLI

# Clear all pools
php bin/ama cache:clear

# Clear specific pools by name
php bin/ama cache:clear --names=cache.pool.config --names=cache.pool.datamodel.data

With no --names option every pool is cleared. Multiple --names values are additive.