Stability & Scaling

Magento Extension Conflicts That Surface Under Load

Some extension conflicts pass QA with one user and only break under load. Here is how concurrency exposes lock and deadlock contention, and how to find it.

Jason Schuman · January 28, 2026

Some conflicts only exist when the store is busy

An extension conflict that shows up in QA is the easy kind. You reproduce it, you trace it, you fix it. The hard kind passes every test with one user and only breaks when real traffic arrives.

These conflicts are not random. They come from resource contention that a single-user test can never trigger.

This article covers why load changes the rules, the specific conflicts that hide until concurrency exposes them, and how to reproduce them before your customers do.

Why load changes the rules

With one request at a time, extensions rarely fight. Each one runs, finishes, and releases whatever it was holding before the next request starts.

Under concurrency, that changes. Requests overlap, and they compete for the same locks, cache keys, database rows, and memory. Two pieces of code that are individually correct can still deadlock or starve each other when they run at the same time.

A feature that passes QA with one user and fails at 500 concurrent users is not a mystery. It is a resource contention problem, and single-user testing structurally cannot see it.

The usual culprits

A handful of patterns account for most load-only conflicts. Each is invisible until requests overlap:

  • Two extensions invalidating each other's cache tags, so every request rebuilds cache the other just cleared.
  • Plugins stacked on the same hot method, fine alone but combining badly when hundreds of requests hit them at once.
  • Database deadlocks on shared tables like quote and inventory, where two extensions update the same rows in different orders.
  • Memory limits that each extension respects alone but exceed together once concurrency multiplies the footprint.

None of these is a bug you can see by reading one code path. They only appear in the overlap.

Session lock contention, the quiet one

The most common load conflict we find is also the least obvious. PHP locks the session while a request holds it, so two requests sharing a session are processed one after another, not in parallel.

An extension that fires many AJAX calls, each opening and writing the session, serializes those calls behind the session lock. With one user testing, the delay is invisible. Under load, the same customer's requests queue up and the store feels slow for reasons the code review never showed.

The fix is to close the session early on requests that do not need to write it, or to stop the extension from writing the session on every call. Both require finding the offender first.

Database deadlocks between extensions

When two extensions write the same rows in different orders, InnoDB will eventually catch them mid-transaction and kill one to break the deadlock. The killed request throws an error the customer sees.

These are intermittent by nature, because they need the two transactions to overlap on exactly the wrong rows. That is why they never reproduce on demand in QA.

Reading SHOW ENGINE INNODB STATUS right after a deadlock shows the two transactions involved and the rows they fought over. That output usually names the tables, which points you at the responsible modules.

Memory pressure multiplies under concurrency

Memory is the constraint teams forget when adding extensions. Each one measures fine in isolation, using a slice of the PHP memory_limit on a single request.

Under concurrency the server runs many PHP workers at once, and their memory adds up against the total the box has, not the per-request limit. A set of extensions that each look reasonable can push the server into swap, or trigger the out-of-memory killer, only when enough requests run together.

That failure looks like random 500s and killed processes, with nothing wrong in any single request. Watching memory during a load test is the only way to catch it before production does.

How to reproduce it before customers do

The only reliable way to surface a load conflict is to create load. A staging environment and a load-testing tool such as siege, k6, or Locust let you push production-like concurrency at the store.

While the test runs, watch the systems that show contention. SHOW FULL PROCESSLIST reveals queries piling up, the slow query log captures what stalls, and SHOW ENGINE INNODB STATUS reports deadlocks as they happen.

If you have application performance monitoring in place, its transaction traces will point at the exact method where requests spend their time waiting. That is often the fastest route to the conflicting code.

Isolating the offender

Once you can reproduce slowdown or errors under load, isolation is mechanical. Disable one suspect extension, run the same load test, and compare.

When the numbers change, you have found a participant in the conflict. Knowing which extensions fight under load, and why, is exactly the kind of finding that separates a store that survives a traffic spike from one that goes down during its busiest hour.