We have a query, which is trying to update a row, placing a user at the end of a queue.
To do this, it needs to grab the MAX(initialPositon) and set this row's initialPosition to MAX(initialPosition)+1
Due to the mysql restrictions on updating the same table you are reading from, we've designed this using a join
It's important this query is atomic, because it could be hit under heavy concurrency and we don't want the position to be duplicated
Here's the query:
UPDATE ListingQueue LQ
JOIN (
SELECT
(MAX(initialPosition) + 1) AS maxpos,
listingid
FROM
ListingQueue
WHERE
ListingQueue.listingid = 945877
FOR UPDATE
) AS LQ2 ON LQ2.listingid = LQ.listingid
SET LQ.initialPosition = LQ2.maxpos,
LQ.stage = 'waitingroom',
LQ.stageLastUpdate = NOW()
WHERE
LQ.token = '6HMQ2JPcMTxUcJquw9SFH9ZQ76@CRbBf6p'
AND LQ.listingid = 945877
This works fine, under normal conditions. When we hit it with concurrency, we get deadlocks:
**Deadlock found when trying to get lock; try restarting transaction**
Any suggestions on how to avoid this lock?
Table Structures:
CREATE TABLE `ListingQueue` (
`token` varchar(100) NOT NULL,
`listingid` int(11) unsigned NOT NULL,
`initialPosition` int(11) unsigned DEFAULT NULL,
`stage` enum('waitingroom','inqueue','eventpage','frontofqueue') DEFAULT 'waitingroom',
`stageLastUpdate` datetime DEFAULT NULL,
`enteredQueue` datetime DEFAULT NULL,
PRIMARY KEY (`token`,`listingid`),
KEY `currentPos` (`listingid`,`initialPosition`,`stage`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
SELECT MAX(...)+1
to generate sequences. Useauto_increment
instead.