2

My understanding is that Apache creates a separate PHP process for each incoming request. That means that if I have code that does something like:

  1. check if record exists
  2. if record doesn't exist, create it

Then this is susceptible to a race condition, is it not? If two requests come in at the same time, and they both hit (1) simultaneously, they will both come back false, and then both attempt to insert a new record.

If so, how do people deal with this? Would creating a MySQL transaction around those 2 requests solve the issue, or do we need to do a full table lock?

mpen
  • 237,624
  • 230
  • 766
  • 1,119

4 Answers4

2

As far as I know you cannot create a transaction across different connections. Maybe one solution would be to set column you are checking to be unique. This way if two connections are made to 10, and 10 does not exist. They will both try to create 10. One will finish inserting the row first, and all is well; then the connection just a second behind will fail because the column isn't unique. If you catch the exception that is thrown, then you can subsequently SELECT the record from the database.

Sam
  • 18,756
  • 2
  • 40
  • 65
1

Honestly, I've very rarely run into this situation. Often times it can be alleviated by reevaluating business requirements. Even if two different users were trying to insert the exact same data, I would defer management of duplicates the users, rather than the application.

However, if there were a reason to enforce a unique constraint in the application logic, I would use an INSERT IGNORE... ON DUPLICATE KEY UPDATE... query (with the corresponding UNIQUE index in the table, of course).

landons
  • 9,374
  • 3
  • 31
  • 46
0

I think that handling errors on the second step ought to be sufficient. If two processes try to create a record then one of them will fail, as long as youve configured the MySQL table appropriately. Using UNIQUE across the right fields is one way to do the trick.

Octopus
  • 7,099
  • 4
  • 36
  • 59
0

Apache does not "create a separate PHP process for each incoming request". It either uses a pool of processes (default, prefork mode), or threads.

The race conditions, as you mention, may also be refered to (or cause) DB "Deadlocks". @see what is deadlock in a database?

Using transactions where needed should solve this problem, yes.
By making sure you check if a record exists and create it within a transaction, the whole operation is atomic.
Hence, other requests will not try to create duplicate records (or, depending on the actual queries, create inconsistencies or enter actual deadlocks).

Also note that MySQL does not (yet) support nested transactions: You cannot have transactions inside transactions, as the first commit will commit everything.

Community
  • 1
  • 1
J.C. Inacio
  • 4,274
  • 2
  • 19
  • 24