I have the following minimal example showing my problem:
I have a Person
table, which has an (auto-increment) id as primary key and some information about the Person.
I have a Widget
table, which likewise has an (auto-increment) id is primary key and some non-unique other information.
I have a Person_Widget
table that maps Persons
to Widget
. This is supposed to be a 1-to-N relation ship where N must be max-limited to 5. A person should have at most 5 widgets (and can also have zero).
How can I enforce this in a concurrency-safe way?
If 2 transactions which intend to add Widgets to a Person both do a SELECT COUNT(...)...
first to determine how many it might still insert, both can read 3 and determine "I can still insert two more widgets for this person", they might both insert two more, leading to a total of 7. This is a classic race condition.
Determining the current COUNT per person with a SELECT ... FOR UPDATE will not help because as determined in this thread, How do I lock on an InnoDB row that doesn't exist yet? , it is not reliable for locking non-existent rows, which will be a problem at least in the case where the Person has zero widgets before the two concurrent transactions start.
My solution: I add a num_widgets column to the person table. Before modifying Person_Widget entries for a person, a transaction must do SELECT num_widgets FROM Person WHERE Person.id=X FOR UPDATE;
. The transaction will also keep this num_widges updated within the transaction so COUNT() isn't necessary anymore.
I.e. I am using the Person table as what I think is referred to as "semaphore table". Is this a bad practice? Coming from a non-database concurrency programming background, this seems a straightforward and intuitive solution, but I've hardly seen anyone talk about it in my very limited MYSQL experience.
Before determining this question to be a duplicate, please consider that the other similar questions with the problem of limiting a table to X rows per key do not consider concurrency and locking at all. It's a whole different matter when concurrecny is considered.