Is there a race condition if two processes both execute this query at (nearly) the same time?
delete
table
from
table with (readpast)
join (
select top (1)
notPK
from
table with (readpast)
order by
table.id
) as selected on
table.notPK = selected.notPK
Assume there are a variety of duplicate notPK
's in table
, that id
is (of course) an identity and primary key, and that there is no shortage of records in this table. Also assume the default isolation level (read committed).
In other words, is there a chance that one of those processes will not get to delete any records from table
even when there's no shortage of records there?
In other words, when does SQL Server apply row locks on the records that will get deleted by the query--before or after they've been SELECTed in that subquery?
I mentioned this in a comment, but the design is to process a First-In-First-Out queue that contains duplicates. In the real world notPK
uniquely identifies a unit so in the ideal world it would be the primary key, but here in the real world there are duplicate entries in the queue for each unit. So when one unit gets dequeued then all the duplicates need to be dequeued as well, because each unit should be processed only up to once.
So the question I'm wondering is whether this query guarantees that a unit (and its duplicates) will always be deleted (when there are records in the table), or if it is sometimes possible for zero records to be deleted. For example, does it do this?
- Read a
notPK
- Get references to all records with that
notPK
and that aren't locked, and place locks on them - Delete those records
- Release the locks
...or does it do this?
- Read a
notPK
and get references for all records with thatnotPK
and place locks on all of those records, all in an atomic operation - Delete those records
- Release the locks
My gut feeling is that it does the former. Therefore two processes could both read the same notPK
, but only one of those processes would "win" the race and acquire locks to delete. The other would end up deleting zero records.
Is that correct?
Here's the same query written a little different with the help of @SlavaMurygin:
delete
table
from
table with (readpast)
where
notPK in (
select top (1)
notPK
from
table with (readpast)
order by
table.id
)
notPK
would be the PK, but it's not and I can't control that. So dequeuing and processing onenotPK
should count as dequeuing the duplicates, and anotPK
should only be processed once. I'm open to design suggestions, but I think the question would still stand. P.S. changed to selectnotPK
delete table from table with (readpast) WHERE notPK in (select top (1) notPK from table with (readpast) order by table.id)