I am writing a procedure that will be reconciling finical transactions on a live database. The work I am doing can not be done as a set operation so I am using two nested cu
If you are only worried about other readers, then you shouldn't need exclusive locks, the pattern
Begin Transaction
Make Data Inconsistent
Make Data Consistent
Commit Transaction
Should be fine. The only sessions who will see inconsistent data are those that use nolock
or Read Uncommitted
, or those that expect to make multiple consistent reads without using Repeatable Rows
or Serializable
.
In answer to the question, the correct way to take an exclusive lock, in my opinion, is to arrange things so the engine does it for you.
I couldn't believe that an XLOCK
would not block a concurrent reader at read committed
so I just reproduced it: It is true. Script:
Session 1:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
BEGIN TRAN
SELECT * FROM T WITH (ROWLOCK, XLOCK, HOLDLOCK /*PAGLOCK, TABLOCKX*/) WHERE ID = 123
Session 2:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
BEGIN TRAN
SELECT * FROM T WHERE ID = 123
Plug in some table name that you have at hand. Session 2 is not being blocked.
I also tried using a PAGLOCK
but that didn't work either. Next I tried a TABLOCKX
but that didn't work either!
So your table-lock based strategy does not work. I think you'll have to modify the readers so that they either
Of course there is a nasty workaround to really, really lock the table: alter its schema. This will take a Sch-M
lock which conflicts with basically any access to the table. It even holds of some metadata read operations. It could look like this:
--just change *any* setting in an idempotent way
ALTER TABLE T SET (LOCK_ESCALATION = AUTO)
I tested this to work.
Is SQL Server right not obeying the XLOCK
? Or is this a flaw in the product? I think it is right because it conforms to the documented properties of READ COMMITTED
. Also, even using SERIALIZABLE
there are cases where one transaction can lock a row exclusively and another can read the same row! This can happen in the presence of indexes. One transaction might X-lock on the non-clustered index IX_T_SomeCol
while another happily reads off of the clustered index PK_T
.
So it is actually quite normal that transactions can execute independently even in the presence of exclusive locking.