ARTICLE

Magento Indexer Update on Schedule vs Update on Save: How mview Changelogs Work and When to Switch

Magento Indexer Update on Schedule vs Update on Save: How mview Changelogs Work and When to Switch

Magento Update on Save reindexes immediately on every data change, so the save carries that cost live. Update on Schedule queues changed entity IDs in MySQL-trigger-fed changelog tables that cron processes in batches. On any store with real catalog volume or frequent saves, Update on Schedule is the correct default, moving reindex work off the save path.

Indexer mode is one of those settings that looks trivial and quietly decides whether your admin saves feel instant or lock up for thirty seconds. The two modes are not just a performance toggle; they use completely different machinery. This guide explains what each mode actually does under the hood, how the mview changelog system tracks changes, what cron does on every run, why changelog tables sometimes grow without bound, and how to switch modes on a live Magento and Adobe Commerce store without causing a reindex storm.

The two indexer modes

A Magento indexer transforms raw data (products, prices, categories, inventory) into flat tables the storefront can read quickly. The mode controls when that transformation runs. Adobe’s indexing overview documents both modes.

Update on Save Update on Schedule
When reindex runs Immediately, inside the save Later, in a cron batch
Mechanism Save triggers reindex of affected entities MySQL triggers log IDs to a _cl table; cron reindexes them
Admin save speed Slow on large catalogs Fast, cost deferred
Risk Save timeouts, lock contention Stale data until cron runs; changelog backlog
Right for Tiny catalogs, dev environments Production stores of any real size

The CLI reports the current mode with bin/magento indexer:show-mode. Each indexer can be in a different mode, which matters more than most teams realize.

How Update on Save works and where it hurts

In Update on Save, changing data that an indexer depends on triggers a reindex of the affected entities as part of that same request. Save a product and the price, category-product, and search indexes for that product refresh before the save completes. On a small catalog this is fine and keeps the storefront perfectly current.

The pain appears at scale. A single product save can cascade into reindexing across several indexers, and a bulk action, such as updating 5,000 products or changing a category assignment, multiplies that cost into the request. You see admin saves that time out, import jobs that crawl, and database lock contention when several saves hit the same index tables at once. Update on Save puts the full reindex cost on the critical path of the person clicking Save, which is exactly the wrong place for it on a busy store.

How Update on Schedule works: mview, triggers, and the _cl table

Update on Schedule decouples the data change from the reindex using Magento’s materialized view (mview) system. Instead of reindexing on save, Magento records which entities changed and reindexes them later in a cron batch.

The declaration lives in mview.xml. Here is the shape of the catalog category-product view:

<!-- Magento/Catalog/etc/mview.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Mview/etc/mview.xsd">
    <view id="catalog_category_product"
          class="Magento\Catalog\Model\Indexer\Category\Product"
          group="indexer">
        <subscriptions>
            <table name="catalog_category_entity" entity_column="entity_id" />
        </subscriptions>
    </view>
</config>

In the view node, id is the indexer table name, class is the indexer executor, and group is the indexer group. Each table under subscriptions names a database table to watch and the entity_column that identifies the entity to reindex.

When an indexer is set to schedule mode, Magento creates a changelog table named after the index table plus _cl, for example catalog_category_product_cl. That table has two columns that carry the whole mechanism: version_id, an auto-increment that orders changes, and entity_id, the identifier of the row that needs reindexing.

Magento then creates MySQL AFTER triggers on each watched table for INSERT, UPDATE, and DELETE. INSERT and DELETE triggers use INSERT IGNORE to write NEW.entity_id or OLD.entity_id into the _cl table. UPDATE triggers are smarter: they only log when a column the indexer cares about actually changes, such as parent_id, path, position, or level on a category. So editing a field the index does not depend on writes nothing to the changelog, which keeps the backlog lean.

The result is that a save becomes cheap. The trigger fires, one row lands in a _cl table, and the request returns. The expensive reindex happens later, off the save path. The source for this machinery is visible in the Magento 2 codebase under each module’s etc/mview.xml.

What cron actually does on each run

The indexer cron group runs the mview update. Internally, Magento\Framework\Mview\ViewInterface::update reads the _cl table for all version_id values greater than the last one it applied, collects the distinct entity_id values, and hands them to the indexer’s execute method for a partial reindex. After processing, it advances the stored version pointer so those rows are not processed again.

Two state tables track all of this. mview_state holds each view’s last applied version_id and its mode, and indexer_state holds each indexer’s status. The status values you will see are valid (Ready in the admin, data is synchronized), invalid (Reindex Required, source data changed), and working (Processing, a reindex is in progress). A partial reindex that hits an error or a very large backlog can also end up suspended, which stops it processing until it is resumed.

Because cron drives the whole thing, a broken or throttled cron is the number one cause of stale storefront data in schedule mode. If prices or stock look wrong on the frontend but correct in the admin, check cron before you touch the indexers. We go deeper on that failure mode in Adobe Commerce cron and indexer health.

When changelog tables balloon

The _cl tables are self-cleaning only as long as cron keeps up. When cron stops, stalls, or cannot process entries as fast as triggers add them, the changelog grows without bound. A category change that touches thousands of product-category relationships, a large import, or a mass attribute update can each dump a huge batch of rows into a _cl table in seconds.

An oversized changelog has knock-on effects. The partial reindex has more work to do each run, which makes it slower, which lets the backlog grow further. If the backlog gets large enough or a reindex errors out, the index can suspend itself to avoid overwhelming the server, and the storefront then serves stale data until someone intervenes. The fix is rarely to delete _cl rows by hand. The safer path is to confirm cron is healthy, run a full reindex of the affected indexer to reset it, and address whatever bulk operation caused the flood. The price indexer is especially prone to this at scale, which we cover in Magento price indexing at scale.

Switching modes safely

Switch modes from the CLI:

# See current modes
bin/magento indexer:show-mode

# Move everything to schedule mode
bin/magento indexer:set-mode schedule

# Move a single indexer back to on-save
bin/magento indexer:set-mode realtime catalog_product_price

Adobe’s manage the indexers reference lists every indexer CLI command and its options. Two cautions when switching. First, moving an indexer into schedule mode creates its triggers and _cl table, and moving it to realtime drops them. On a large database these DDL operations can take time and briefly contend with live traffic, so do the switch during a quiet window. Second, after switching to schedule mode the index is not automatically current; run bin/magento indexer:reindex once to establish a clean baseline, then let cron maintain it. Verify cron is actually running on the box before you rely on schedule mode, because a store in schedule mode with dead cron is a store serving increasingly stale data.

Which mode for which indexer

For production, schedule mode is the right default for nearly every indexer, because it keeps saves fast and lets cron batch the work. As of recent releases, new indexers default to Update on Schedule, and the customer grid indexer supports both modes. Keep realtime only where immediacy genuinely matters more than save performance, which on most stores is nowhere. In development, realtime can be convenient so you see changes without waiting for cron, but never carry that choice into production on a catalog of any size. This is part of the same performance discipline behind full page cache warming on Magento and Hyva: keep the expensive work off the request the customer or the admin is waiting on.

Bemeir is the first US-based Hyva development partner and a full Magento and Adobe Commerce shop, and we apply the same operational rigor across Shopify, Shopware, and BigCommerce builds. Our technology partner ecosystem means these stores run heavy catalogs with ERP and pricing integrations writing to them constantly, so getting indexer modes right is a daily reality, not a tuning afterthought. You can read more about Bemeir and how we keep stores fast.

FAQ

What is the difference between Update on Save and Update on Schedule in Magento?

Update on Save reindexes affected entities immediately as part of the save request, which keeps data current but slows saves and imports on large catalogs. Update on Schedule records changed entity IDs in a changelog (_cl) table through MySQL triggers and reindexes them later in a cron batch, keeping saves fast at the cost of a short delay before the storefront reflects changes.

What is the _cl table in Magento?

A _cl table is the mview changelog for an indexer running in Update on Schedule mode, named after the index table plus _cl, such as catalog_category_product_cl. It has a version_id auto-increment column that orders changes and an entity_id column identifying rows to reindex. MySQL AFTER triggers write to it on INSERT, UPDATE, and DELETE, and cron reads new rows and runs a partial reindex.

Why is my Magento changelog table so large?

Because cron is not processing it fast enough, or at all. In schedule mode, triggers keep adding rows while cron removes processed ones, so if cron is broken, throttled, or overwhelmed by a bulk operation, the _cl table grows unchecked. Confirm cron is healthy, run a full reindex of the affected indexer to reset the baseline, and fix the bulk process that flooded it rather than deleting changelog rows manually.

How do I change indexer mode in Magento from the command line?

Use bin/magento indexer:set-mode schedule to move all indexers to Update on Schedule, or pass an indexer ID like bin/magento indexer:set-mode realtime catalog_product_price to change one. Check current modes with bin/magento indexer:show-mode. After switching to schedule, run bin/magento indexer:reindex once to establish a clean baseline, and make sure cron is running.

Should I use Update on Schedule in production?

Yes, for nearly every indexer on any store with real catalog volume or frequent data changes. It keeps admin saves and imports fast by deferring reindex work to cron. The one requirement is a healthy cron, because schedule mode depends on it; a store in schedule mode with dead cron will serve increasingly stale prices, stock, and category data.

Let us help you get started on a project with Magento Indexer Update on Schedule vs Update on Save: How mview Changelogs Work and When to Switch and leverage our partnership to your fullest advantage. Fill out the contact form below to get started.

more articles about ecommerce

Read on the latest with Shopify, Magento, eCommerce topics and more.