(Please answer as generally as possible. But I am working in MS SQL Server and MySql, so if there is no general answer, go ahead and answer for one or both of those.)
Consider a reservation system implemented in a SQL database. I want to make sure that among many multiple users, only one user gets a reservation and no other user "thinks" they got it. It's a classic concurrency problem in DB work, but I'm not sure what the best answer is.
SPECIFICS:
Suppose each user has a UserID. We could imagine a few users are currently trying to make a reservation with UserID values of 1004, 1005, 1009, and 1011.
Suppose the resource and reservations are stored in a table, SEATS . We could imagine at one point the SEATS table contains:
----- SEATS -----------------------------
SeatID UserID ResvTime
1 1017 2014.07.15 04:17:18.000
2 NULL NULL
3 NULL NULL
4 1012 2014.07.15 04:19:35.000
5 1003 2014.07.15 04:20:46.000
-----------------------------------------
Now suppose that "at the same time", users 1004 and 1005 try to grab SeatID 3. I'm wanting to know what SQL will properly make sure that only one of them gets the seat and the other gets a refusal. The simplest version of the code I can think of, in T-SQL, would be:
PROC GRABSEAT @seatid INT, @userid INT, @obtained BIT OUTPUT
BEGIN
DECLARE @avail INT
SET @avail = (SELECT UserID FROM SEATS WHERE (SeatID = @seatid))
IF (@avail IS NULL)
BEGIN
UPDATE SEATS SET UserID = @userid, ResvTime = GETDATE() WHERE (SeatID = @seatid)
SET @obtained = 1
END
ELSE
SET @obtained = 0
END
But the question is how to prevent this from allowing multiple concurrent users, all executing this PROC, getting a TRUE return on the same seat (say SeatID = 3).
For example, if both users 1004 and 1005 execute this PROC nearly simultaneously, they could both do the SELECT and get @avail = NULL before either of them tries to set the UserID column. Then both of them would run the UPDATE statement. Assuming nothing even worse could result, then one of them would overwrite the other's set, both would think they got the seat, but actually only the one who ran the UPDATE statement last would have their data stored in the SEATS table. The other one would have their data overwritten. This is referred to as a "lost input" problem. But what is the way in a SQL database to prevent it? I have been assuming that each single SQL statement is executed as a TRANSACTION. A TRANSACTION has the four so-called "ACID" properties. These properties are what I need. So, I think the answer, in a SQL database, is:
BEGIN TRANSACTION
EXCEUTE GRABSEAT @seatid= <id1>, @userid = <id2>, @obtained
COMMIT
By doing so, the main property I need (isolation), will guarantee that the interleaved execution I'm worried about will not occur.
But I've seen articles that say it's not that simple at all. I think the big problem various articles point to is that not every TRANSACTION really runs in total atomicity and isolation. So, perhaps the above wrapping in a TRANSACTION will not achieve the desired result. If not, then what is needed?