ARTICLE

MySQL Deadlocks and Lock Wait Timeouts in Magento 2: Why They Happen and How to Fix Them on Adobe Commerce

MySQL Deadlocks and Lock Wait Timeouts in Magento 2: Why They Happen and How to Fix Them on Adobe Commerce

In Magento 2, MySQL error 1213 (“Deadlock found when trying to get lock”) means InnoDB found two transactions waiting on each other and rolled one back. Error 1205 (“Lock wait timeout exceeded”) means a transaction waited longer than innodb_lock_wait_timeout, 50 seconds by default. Both usually point to overlapping indexers, cron jobs, imports or custom code, not to a broken database.

That distinction is the whole game. Most of the advice in forum threads, such as raising timeouts, truncating cron_schedule, or adding a retry module, treats the symptom. This guide explains what each error actually means, how to find the two transactions that collided, and which Magento processes cause most of these collisions on Adobe Commerce and Magento Open Source.

Deadlock vs lock wait timeout: two different problems

People use the words interchangeably, but MySQL treats them very differently, and the fix depends on which one you have.

Deadlock (error 1213) Lock wait timeout (error 1205)
Error text SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction SQLSTATE[HY000]: General error: 1205 Lock wait timeout exceeded; try restarting transaction
What happened Two transactions each hold a lock the other needs. InnoDB detects the cycle instantly and rolls one back One transaction waited for a lock held by another until the timeout ran out
How long the shopper or job waits Milliseconds, the victim fails almost immediately Up to innodb_lock_wait_timeout (default 50 seconds)
What is rolled back The whole victim transaction By default only the statement that timed out, not the whole transaction
Typical cause Two writers touching the same rows in different order One long transaction holding locks while others queue behind it
Safe to retry? Yes, MySQL says to retry Usually, but find the long holder first

The rollback behavior in the last rows matters for data integrity. With innodb_rollback_on_timeout left at its default of OFF, a 1205 rolls back only the last statement, and the rest of the transaction is still open. Code that catches the error and carries on can commit a half-finished change. The MySQL InnoDB startup options and system variables reference documents both variables.

Why Magento is prone to lock contention

Magento does a lot of concurrent writing to a small number of hot tables. A few patterns cause most of the trouble:

  • Indexers running on save during business hours. In “Update on Save” mode, every product, price or stock change in the Admin triggers index writes inside the same request. When an import, a mass action and a cron indexer run at once, they compete for the same index rows.
  • Full reindexes while the store takes orders. A full indexer:reindex rebuilds large tables and can hold locks long enough for checkout queries to queue behind it.
  • Imports and mass updates on EAV tables. Product imports write to many catalog_product_entity_* tables row by row. Two import processes, or an import plus an Admin save, can lock rows in different orders.
  • Quote and order writes at checkout. High order volume means many transactions writing quote, sales_order and inventory rows at the same moment, especially during a sale.
  • Cron jobs that overlap. Price rule application (catalogrule_apply_all), indexer cron and custom jobs can all run in the same minute. If one runs long, the next run starts while it is still holding locks.
  • Custom and third-party code. Long transactions that call an external API while holding locks, or loops that save one entity at a time inside a transaction, are a frequent cause.

Adobe itself has shipped fixes in this area. Quality patch ACP2E-4913 addresses REST API shipment and invoice creation that is not retried after a database deadlock, which surfaced as SQLSTATE[40001] error 1213 and left the shipment or invoice to be created by hand. Adobe lists it for 2.4.7 through 2.4.8-p5, with the fix planned for 2.4.10. If your ERP or WMS creates shipments over REST and you see those errors, check that patch before writing your own workaround.

Step 1: find the transactions that collided

Do not change a setting until you know which two queries deadlocked. MySQL gives you the evidence for free.

Read the latest deadlock. Run this as a privileged MySQL user:

SHOW ENGINE INNODB STATUS\G

Find the LATEST DETECTED DEADLOCK section. For each transaction it shows the statement that was running, the lock it held (HOLDS THE LOCK(S)), the lock it wanted (WAITING FOR THIS LOCK TO BE GRANTED), the table and index, and which transaction was rolled back. Two things to note every time: the table and index named, and the two SQL statements. Those usually identify the Magento process straight away. A catalog_product_index_price table points to price indexing, a cron_schedule table points to overlapping cron, an inventory_reservation table points to checkout.

Capture every deadlock, not just the last one. SHOW ENGINE INNODB STATUS only keeps the most recent. The MySQL guide to minimizing and handling deadlocks recommends enabling innodb_print_all_deadlocks while you investigate, which writes each one to the MySQL error log, and turning it off when you are done.

SET GLOBAL innodb_print_all_deadlocks = ON;

On managed databases such as Amazon RDS or Adobe Commerce Cloud, you may need to change it in a parameter group or ask support, and read it from the database error log rather than the server.

Match it against Magento’s logs. Search var/log/exception.log and var/log/system.log for 1213 and 1205, and check var/log/cron.log for jobs running at the same timestamp. The combination of “which tables” from MySQL and “which job” from Magento is nearly always enough to name the cause.

For lock wait timeouts, find the blocker while it is happening. A 1205 is caused by a transaction that is still running. On MySQL 8, sys.innodb_lock_waits shows who is waiting on whom, and information_schema.innodb_trx shows how long each open transaction has been running. A transaction that has been open for minutes is your suspect.

Step 2: fix the cause, by source

Once you know the source, the fix is usually one of these.

Source in the deadlock output Typical Magento cause Fix
*_index_*, *_cl changelog tables Indexers on “Update on Save”, or full reindex during trading Switch to “Update by Schedule”, run full reindexes off-peak
cron_schedule Cron runs overlapping, or several cron processes started at once One cron entry per server, check for stuck jobs, separate cron groups
catalogrule_* Price rule application colliding with indexers or imports Run rule application off-peak, avoid mass rule saves during sales
catalog_product_entity_* Concurrent imports or mass actions Serialize imports, smaller batches, avoid Admin mass edits during imports
quote, sales_order, inventory_reservation Checkout under load, custom observers on order placement Shorten checkout transactions, move work after commit or to a queue
Custom module tables Long transactions in your own code Smaller transactions, consistent lock order, retry logic

Move indexers to Update by Schedule. This is the single most effective change on most stores. In schedule mode, saves only write a row to a changelog table, and the cron indexer processes changes in batches, so Admin users and imports stop doing index work inline.

bin/magento indexer:set-mode schedule
bin/magento indexer:status

Our explainer on indexer Update on Schedule vs Update on Save and mview changelogs covers what changes and the few cases where on-save still makes sense.

Stop cron from piling up. Make sure only one system crontab entry runs bin/magento cron:run per server, and that a long job is not still running when the next minute starts. If indexer cron, consumers and custom jobs all share the default group, give heavy jobs their own group with use_separate_process. Our guide to Adobe Commerce cron and indexer health walks through spotting a backed-up queue.

Keep big tables small. Deadlocks get more likely as scans touch more rows. Bloated quote, log and changelog tables make every lock last longer. See our piece on Magento database bloat and quote cleanup for what is safe to prune.

Fix custom code. MySQL’s own guidance applies directly to Magento modules:

  • Keep transactions small and short. Do not wrap a whole batch import in one transaction.
  • Never call an external API, send an email or wait on a queue while a transaction is open. Commit first, then do the slow work.
  • Touch tables and rows in the same order everywhere. If one code path updates stock then price and another updates price then stock, they will eventually deadlock.
  • Add indexes for the columns your UPDATE and DELETE statements filter on, so InnoDB locks fewer rows. Check with EXPLAIN.
  • Use SELECT ... FOR UPDATE only when you truly need it.

Step 3: add retries where they belong

MySQL’s documentation is blunt that deadlocks are not dangerous and that applications should always be ready to retry a transaction that fails with one. Retrying is correct for 1213, because the victim’s work was fully rolled back.

A minimal retry wrapper in a custom service looks like this:

$attempts = 0;
while (true) {
    $connection->beginTransaction();
    try {
        $this->doWork();
        $connection->commit();
        break;
    } catch (\Exception $e) {
        $connection->rollBack();
        $isDeadlock = str_contains($e->getMessage(), '1213');
        if (!$isDeadlock || ++$attempts >= 3) {
            throw $e;
        }
        usleep(100000 * $attempts);
    }
}

Three rules keep retries safe. Retry the whole transaction, not the single statement. Cap the attempts and back off between them. Make sure the work inside is idempotent, so a retry cannot double-charge, double-ship or send two emails. Recent Magento versions map these MySQL errors to their own exception classes in the DB adapter layer, so check what your version throws and catch that rather than matching strings where you can.

Retries are a safety net, not a fix. If the error log shows hundreds of deadlocks an hour, retries will just add load. Go back to step 2.

Settings to change, and ones to leave alone

innodb_lock_wait_timeout. Default 50 seconds. Raising it does not fix anything. It only makes shoppers and jobs wait longer before failing. Some teams lower it for web traffic so requests fail fast instead of holding a PHP-FPM worker for nearly a minute. It can be set per session, so a long-running import connection can keep a higher value while web requests use a lower one.

wait_timeout. A common forum answer suggests setting this in env.php through initStatements. It controls idle connections, not row locks, and will not affect a 1205.

innodb_rollback_on_timeout. Turning it on makes a 1205 roll back the whole transaction, which is safer for data consistency. It requires a server restart and should be tested, since code that relied on statement-level rollback will behave differently.

Transaction isolation. MySQL suggests READ COMMITTED as an option when locking reads cause deadlocks, because it reduces gap locking. It changes visibility semantics for every query, so test it on staging with real cron, import and checkout load before considering production.

Truncating cron_schedule. It can clear a stuck backlog, but it does not prevent the next deadlock, and you lose the history you need to diagnose it. Take a backup and treat it as a reset, not a fix.

How Bemeir approaches database contention

Bemeir is a Brooklyn agency for Magento, Adobe Commerce and Hyva. Lock contention is a backend problem, so it sits squarely in Magento development services work: reading the deadlock output, mapping it to the indexer, cron, import or module involved, and fixing that source rather than raising a timeout. A faster frontend from Hyva development services does not reduce database locking by itself, but it makes slow checkout queries more visible, because the rest of the page is no longer hiding them.

Teams comparing platforms sometimes ask whether hosted options avoid this class of problem. On a Shopify development or BigCommerce development project the platform owns the database, so lock contention moves into the apps and middleware you run. Shopware development runs on MySQL or MariaDB and faces the same InnoDB rules described here. For database hosting and monitoring tools, see the Bemeir technology partners list, and the About Bemeir page for how the team works.

FAQ

What causes “Deadlock found when trying to get lock” in Magento 2?

Two transactions each hold a lock the other needs, so InnoDB rolls one back with error 1213. In Magento this is usually indexers running on save during trading hours, overlapping cron jobs, concurrent imports, checkout under heavy load, or custom code with long transactions.

Should I increase innodb_lock_wait_timeout to fix Magento lock wait timeouts?

No. The default is 50 seconds, and raising it only makes requests wait longer before failing. Find the long-running transaction holding the lock with sys.innodb_lock_waits or information_schema.innodb_trx and fix that process instead.

How do I see which queries caused a deadlock?

Run SHOW ENGINE INNODB STATUS and read the LATEST DETECTED DEADLOCK section, which lists both transactions, their SQL, and the locks held and wanted. Enable innodb_print_all_deadlocks temporarily to log every deadlock to the MySQL error log.

Does switching indexers to Update by Schedule reduce deadlocks?

Usually, yes. In schedule mode a save only writes to a changelog table, and the cron indexer processes changes in batches, so Admin saves, imports and indexing stop competing for the same index rows inside one request.

Is it safe to retry a Magento transaction after a deadlock?

Yes, if the whole transaction is retried, attempts are capped with a backoff, and the work is idempotent. MySQL rolls back the entire victim transaction on a deadlock, so a clean retry is the documented approach.

Let us help you get started on a project with MySQL Deadlocks and Lock Wait Timeouts in Magento 2: Why They Happen and How to Fix Them on Adobe Commerce 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.