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
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.