Does WordPress handle concurrency and atomicity properly?

Does WordPress handle simultaneously requests properly

The question is disingenuous and misleading, and can be rephrased as:

Does WordPress use transactions or locking when making database queries?

For which the answer is no.

Because:

  • When WP was introduced, transactions were not supported by MySQL
  • Locking has major performance implications, and can also depend on the database engine, e.g. MySQL will perform table or row level locks when performing an update, but what you’re referring to happens across multiple queries
  • The only situations in native WP that suffer from the problem you’re asking about are when multiple people open a post and try to save after making conflicting changes, for which there is indeed a post lock saved to prevent this, but it’s trying to prevent editorial conflicts from multiple users, not race conditions and atomicity issues related to concurrency control ( it is not a database lock or mutex ).
  • The risk is very very low for the kind of data processing that WordPress does, and requires code decisions that are considered bad practice.
  • When explicit locking is available there can be a big performance cost

Take a look at this pseudo code:

That is not PHP code, that is SQL, what you’re referring to is not a thread safety issue, it’s an SQL issue, specifically atomic transactions, and it is not unique to PHP applications.

What you are describing is not thread safety, but concurrency control.

In particular it’s possible to construct single process single threaded examples of the problem you’re trying to ask about by using non-blocking requests or async IO.

And no, transactions are not a perfect solution, or usable on a lot of WP installs. WP came from a time when MySQL did not support it, and locking wasn’t reliably present or reliably.

Does WordPress handle simultaneously requests properly or do they really take the risk?

WordPress does not handle simultaneous requests because it is not a single long running process, that’s not how PHP applications work. A request by a user is handled by a single thread in a single self contained process. There are no PHP based shared resources to lock or unlock, unlike other languages that support threads.

Note: Processes are not threads. A process can have a thread, and PHP processes are single threaded. To argue that this makes PHP multi-threaded is to fundamentally redefine the meaning of thread safety and undermine the question.

The question would make more sense if you asked it about a .Net web application or a Node JS web app, but when you make a request to a PHP application these are the broad strokes of what happens:

  • Nginx/Apache is what listens to and receives the request, not WP
  • Nginx/Apache hands the request over to PHP FPM which spins up a new PHP process
  • PHP loads and executes the requested PHP file for that single request, which then handles the request and responds to the user
  • At the end of the request the PHP process is wiped and the ends

Different setups such as CGI-bin vs php-fpm can change this, perhaps by keeping PHP workers around, but there is no long-running PHP program that waits, and importantly PHP has no threads.

SQL Atomic Actions

But if you don’t use a transaction nor a mutex and the code runs two times simultaneously the balance will only be decreased by 10 instead of 20.

Indeed this can happen in any language, and a mutex isn’t reliable either, especially when your database is sharded across thousands of machines in multiple data centres ( these types of setups do exist in the WordPress world ).

WordPress does not use transactions in its database layer, mainly because not all hosts that run WordPress are capable of it, and there are other tradeoffs involved. E.g. if I lock a row, fetch its data, then make a 2s HTTP request, update it, then unlock it, that row is locked and unusable for at least 2s in that example.

Why Do Plugins such as WP Lock exist?

3 reasons:

  • reassurance
  • lack of experience with bugs
  • workarounds

To explain why, here’s an example of a situation I encountered where I might have reached for such a solution but it was not the solution I hoped for, as well as a common workaround made by WP developers while trying to debug these kinds of issues.

A long time ago a site I worked on had a duplication issue with an RSS aggregator that ran in a WP cron job. The prevailing theory was that similar to your question a cron job was starting before the previous cron job had finished creating posts, resulting in 2 aggregation jobs and posts being created twice. Thinking about concurrency file based locks, locks stored as options in the database, etc, were all tried and failed.

The root problem was that it was a load balanced server with 2 machines both running WP Cron, both would fetch and create posts at the same time. The solution was to designate one machine the place for WP Cron to run, and in more modern sites tools such as cavalcade with job queues to hand out tasks.

Final Notes

In practical terms, the kind of data matters enormously, as does performance. For example storing view counts in post meta and incrementing them is awful for performance and inaccurate because of race conditions. The answer is not always locks transactions and mutexes though, there are other solutions such as recording individual views rather than updating totals not totals then calculating those when fetching, or using specialised data structures that outperform mutexes and locks in both speed and reliability.

After 10 years of working with WordPress, and on high end Enterprise clients handling billions of weekly page views, the only time I have seen this become an issue is when users try to store and update counters in post meta, which even with transactions is a very bad idea.