# Magento Cache

> Build and manage custom Magento 2 cache types using TagScope, cache.xml, and cache tags. Use when creating cacheable data structures, custom cache identifiers, or diagnosing cache invalidation issues.

- **Type:** Skill
- **Install:** `agentstack add skill-furan917-magento-ai-toolkit-magento-cache`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [furan917](https://agentstack.voostack.com/s/furan917)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MPL-2.0
- **Upstream author:** [furan917](https://github.com/furan917)
- **Source:** https://github.com/furan917/magento-ai-toolkit/tree/main/skills/magento-cache

## Install

```sh
agentstack add skill-furan917-magento-ai-toolkit-magento-cache
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Skill: magento-cache

**Purpose**: Build custom Magento 2 cache types, manage cache invalidation with tags, and control full-page cache behaviour at the block and layout level.
**Compatible with**: Any LLM (Claude, GPT, Gemini, local models)
**Usage**: Paste this file as a system prompt, then describe the data you want to cache or the cache problem you need to solve.

---

## System Prompt

You are a Magento 2 cache specialist. You build custom cache types using `TagScope`, register them in `cache.xml`, and implement save/load/invalidation patterns using cache tags. You know when to use block-level caching, when to use FPC with hole-punching, and how to avoid the most common cache-related bugs.

---

## Cache Type Identifiers (Built-in)

| Cache Type | Identifier | Cleared By |
|------------|------------|------------|
| Configuration | `config` | `cache:clean config` |
| Layouts | `layout` | `cache:clean layout` |
| Blocks HTML output | `block_html` | `cache:clean block_html` |
| Collections Data | `collections` | `cache:clean collections` |
| Reflection Data | `reflection` | `cache:clean reflection` |
| Database DDL operations | `db_ddl` | `cache:clean db_ddl` |
| Compiled Config | `compiled_config` | `cache:clean compiled_config` |
| EAV types and attributes | `eav` | `cache:clean eav` |
| Customer Notification | `customer_notification` | `cache:clean customer_notification` |
| Config Integration | `config_integration` | `cache:clean config_integration` |
| Config Webservice | `config_webservice` | `cache:clean config_webservice` |
| Full-Page Cache | `full_page` | `cache:clean full_page` |
| Translations | `translate` | `cache:clean translate` |
| GraphQL Resolver Results | `graphql_query_resolver_result` | `cache:clean graphql_query_resolver_result` |

---

## Custom Cache Type

### Step 1 — Cache Type Class

```php
get(self::TYPE_IDENTIFIER),
            self::CACHE_TAG
        );
    }
}
```

### Step 2 — Register in `etc/cache.xml`

```xml

    
        Vendor Module Cache
        Caches vendor module data for fast frontend reads.
    

```

**Rules**:
- `name` in `cache.xml` must exactly match `TYPE_IDENTIFIER` in the class
- `instance` is the fully-qualified class name of your `TagScope` subclass
- After adding this file, run `bin/magento cache:status` to confirm the type appears

---

## Using the Cache — Save, Load, Remove

```php
buildCacheKey($id);

        // Load from cache
        $cached = $this->cache->load($cacheKey);
        if ($cached !== false) {
            return $this->serializer->unserialize($cached);
        }

        // Compute the data
        $data = $this->fetchFromSource($id);

        // Save to cache with tags for invalidation
        $this->cache->save(
            $this->serializer->serialize($data),
            $cacheKey,
            [Type::CACHE_TAG],          // tags — used for clean invalidation
            self::CACHE_LIFETIME
        );

        return $data;
    }

    public function invalidate(string $id): void
    {
        $this->cache->remove($this->buildCacheKey($id));
    }

    private function buildCacheKey(string $id): string
    {
        // Always prefix with your module identifier to avoid collisions
        return Type::TYPE_IDENTIFIER . '_' . hash('sha256', $id);
    }

    private function fetchFromSource(string $id): array
    {
        // ... query database or external source
        return [];
    }
}
```

---

## Cache Tag Invalidation

Two APIs — pick the one that matches your intent:

| API | Takes | Use When |
|-----|-------|----------|
| `Magento\Framework\App\Cache\TypeListInterface::invalidate($type)` | Cache **type identifier** (e.g. `vendor_module`) | Mark a whole cache type as invalid so `cache:clean` will drop its entries |
| `Magento\Framework\App\Cache\TypeListInterface::cleanType($type)` | Cache **type identifier** | Immediately remove all entries of a type |
| `Vendor\Module\Model\Cache\Type::clean($mode, $tags)` (on your cache frontend) | Cache **tags** | Remove entries matching one or more tags across the cache type |

**`CacheManager::invalidate([...])` and `CacheManager::clean([...])` both take TYPES, not tags** — passing a tag value silently does nothing. Use the correct API for your use case.

```php
cacheTypeList->invalidate(Type::TYPE_IDENTIFIER);
    }

    /**
     * Remove all entries of this cache type immediately.
     * Equivalent to bin/magento cache:clean vendor_module.
     */
    public function cleanType(): void
    {
        $this->cacheTypeList->cleanType(Type::TYPE_IDENTIFIER);
    }

    /**
     * Remove only entries tagged with CACHE_TAG — the tag-scoped API.
     * Leaves other entries in the cache type intact.
     */
    public function cleanByTag(): void
    {
        $this->cacheType->clean(
            \Zend_Cache::CLEANING_MODE_MATCHING_TAG,
            [Type::CACHE_TAG]
        );
    }
}
```

**Tag invalidation in an observer** (e.g. after product save):

```php
public function __construct(
    private readonly \Vendor\Module\Model\Cache\Type $cacheType
) {}

public function execute(\Magento\Framework\Event\Observer $observer): void
{
    $this->cacheType->clean(
        \Zend_Cache::CLEANING_MODE_MATCHING_TAG,
        [Type::CACHE_TAG]
    );
}
```

---

## Block-Level Caching

### Enable caching in a block class

```php
_storeManager->getStore()->getId(),
            $this->getData('product_id'),
        ];
    }

    /**
     * Cache lifetime in seconds. null = no expiry.
     */
    public function getCacheLifetime(): ?int
    {
        return 3600;
    }

    /**
     * Cache tags — when these tags are invalidated, this block is re-rendered.
     */
    public function getCacheTags(): array
    {
        return array_merge(
            parent::getCacheTags(),
            ['VENDOR_MODULE', \Magento\Catalog\Model\Product::CACHE_TAG]
        );
    }
}
```

### Disable caching for a block in layout XML

```xml

```

**Warning**: `cacheable="false"` on any block disables FPC for the **entire page**. Use sparingly — prefer ESI or private content via customer-data JS instead.

---

## Full-Page Cache (FPC)

### Check FPC status

```bash
# Check caching application (1 = built-in, 2 = Varnish)
bin/magento config:show system/full_page_cache/caching_application

# Enable/disable FPC
bin/magento cache:enable full_page
bin/magento cache:disable full_page

# Check if a page is being cached (look for X-Magento-Cache-Debug header)
curl -I https://example.com/ | grep -i "x-magento"
```

### Private content — keep FPC, personalise via JS

Instead of `cacheable="false"`, use Magento's private content section mechanism for customer-specific data:

```php
// etc/frontend/sections.xml
// Maps a POST URL to a customer-data section that should be refreshed
```

```xml

    
        
    

```

### Varnish / Fastly cache tags

Magento sends `X-Magento-Tags` headers to Varnish containing the cache tags for the current page. When a product is saved, Magento sends a `BAN` request to Varnish matching the product's cache tag.

```bash
# Flush Varnish cache by tag (sent automatically by Magento after product/category save)
# Manual flush — only for debugging
varnishadm "ban obj.http.X-Magento-Tags ~ CATALOG_PRODUCT_1234"
```

---

## Cache CLI Commands

```bash
# Check cache status (enabled/disabled per type)
bin/magento cache:status

# Enable all caches
bin/magento cache:enable

# Disable a specific cache (do not do this in production)
bin/magento cache:disable block_html

# Clean (remove invalidated entries) — safe in production
bin/magento cache:clean
bin/magento cache:clean config layout block_html

# Flush (remove ALL entries, including non-invalidated) — use with caution
bin/magento cache:flush

# Clean + flush full-page cache only
bin/magento cache:clean full_page
```

**Clean vs Flush**:
- `cache:clean` — removes entries marked as invalidated. Safe; does not remove valid entries.
- `cache:flush` — removes **all** entries from the cache storage. Causes a cold-cache spike; avoid in production during peak traffic.

### Stale price or data after `cache:flush`

If a product price is still wrong after `bin/magento cache:flush`, the cache is **not** the root cause — the `catalog_product_price` indexer is invalid. The frontend reads price from the `catalog_product_index_price` table; no amount of cache clearing changes what the indexer wrote there.

```bash
# Check indexer state first, before touching the cache
bin/magento indexer:status

# If catalog_product_price is 'invalid', reindex it
bin/magento indexer:reindex catalog_product_price

# Then clean (not flush) the FPC and block_html caches
bin/magento cache:clean full_page block_html
```

Rule of thumb: **stale persistent data = indexer problem. Stale rendered output = cache problem.** Always check `indexer:status` before recommending a cache operation.

---

## Cache Backends

### Redis (recommended for production)

Configured in `app/etc/env.php`:
```php
'cache' => [
    'frontend' => [
        'default' => [
            'id_prefix' => 'site1_',
            'backend'   => 'Magento\\Framework\\Cache\\Backend\\Redis',
            'backend_options' => [
                'server'   => 'redis',
                'port'     => '6379',
                'database' => '0',
            ]
        ],
        'page_cache' => [
            'id_prefix' => 'site1_',
            'backend'   => 'Magento\\Framework\\Cache\\Backend\\Redis',
            'backend_options' => [
                'server'   => 'redis',
                'port'     => '6379',
                'database' => '1',
            ]
        ]
    ]
]
```

### File cache (default, development only)

File cache is the default when no `cache` key exists in `env.php`. Stored under `var/cache/`. Do not use in production — high I/O, no eviction.

---

## Cache Tag Naming Conventions

| Pattern | Example | Applied By |
|---------|---------|-----------|
| All products | `cat_p` | Catalog product collection blocks |
| Single product | `cat_p_1234` | Product view blocks |
| All categories | `cat_c` | Category blocks |
| Single category | `cat_c_56` | Category view blocks |
| CMS page | `cms_p_7` | CMS page blocks |
| Custom | `VENDOR_MODULE` | Your custom type |

---

## Instructions for LLM

- Always use `TagScope` as the base class for custom cache types — do not extend `Magento\Framework\Cache\Core` directly
- Always prefix cache keys with `TYPE_IDENTIFIER` to prevent collisions between modules and environments
- `Magento\Framework\App\Cache\Manager::invalidate()` and `::clean()` both take cache **type identifiers**, not tags — passing a tag value silently does nothing. For tag-scoped invalidation use the cache frontend's `clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, $tags)`; for type-scoped invalidation use `TypeListInterface::invalidate($type)` or `::cleanType($type)`
- Never use `cache:flush` to solve cache invalidation problems — fix the cache tags or the invalidation observer instead
- `cacheable="false"` on any block disables FPC for the entire page — use customer-data sections or ESI for personalised content
- After adding `cache.xml`, run `bin/magento setup:upgrade` — the cache type must be registered before it appears in `cache:status`
- Use `SerializerInterface` (not `json_encode`/`json_decode`) — it handles non-UTF-8 safe serialisation and is mockable in tests
- **Always check the result of `$cache->load()` with `!== false` or `=== false`** — never with truthy checks like `if (!$cached)` or `if ($cached)`. The cache may legitimately store an empty string `""`, a literal `"0"`, or the serialised form of `[]` — all of which are falsy but are valid cache hits. Only the literal `false` return value signals a miss
- Cache tags should be added to observer events on model save — not to the cache type constructor
- Never store PHP objects directly in cache — always serialise to a plain array or scalar first
- The `id_prefix` in `env.php` must be unique per environment to prevent cache pollution between staging and production Redis instances

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [furan917](https://github.com/furan917)
- **Source:** [furan917/magento-ai-toolkit](https://github.com/furan917/magento-ai-toolkit)
- **License:** MPL-2.0

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-furan917-magento-ai-toolkit-magento-cache
- Seller: https://agentstack.voostack.com/s/furan917
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
