Question

I have 2 batch programs, 1 is a program that sends email and another one sends fax. They both access a table named QUEUE.

In the email sender program, this is what happens in relation to QUEUE. For each record in QUEUE that satisfies criteria:

  1. Locks record 1 in QUEUE table:
    select 1 from QUEUE with (UPDLOCK) where id = 1
  2. Process sending out of email
  3. Delete record 1 in QUEUE table:
    delete from QUEUE where id = 1
  4. commit transaction (transaction is not auto-commit)

In the fax sender program, similar steps also happen, except that in step 2, we send out fax (of course).

The problem is that sometimes the delete from QUEUE throws out an exception that it is locked. Thereby, re-sending of the emails/fax happens. I am sure that the group of records processed by these programs don't intersect.

It seems that delete tries to get an Update (U) lock on other records in the table although only one record is to be deleted. So exception happens when other transactions have locks on other records in that same table.

With that, I need to know if there's an option to make "delete" operation not acquire locks on records other than for the record it will delete. Because it seems that the problem is with the "delete" operation taking locks on other records in the table.

By the way, here are some information about the database (I'm not sure if they will help):

  • Read Committed Snapshot is turned on
  • Snapshot Isolation State is on
Was it helpful?

Solution

Have you tried using the WITH ROWLOCK or WITH NOLOCK hint on the delete statement?

Have you read this article? it suggests you use (UPDLOCK, READPAST) to prevent the issue your encountering

OTHER TIPS

Sometimes a lock escalades from locking a single row into locking a section of a table or locking an entire table. That is most likely why you are getting locks on records that you are not actually using in the transaction.

Instead of using a transaction to lock the records, you could use a status field to flag the records that are processed.

Example:

Lock some records, then get the record that was successfully locked:

update queue set status = 'email_processing' where status is null and id = 1
select email, message from queue where status = 'email_processing'

When you are not explicitly using a transaction each query runs in it's own transaction, so the update query can safely change the status as it's verifying the current status in the same query.

When done sending, delete the records:

delete from queue where status = 'email_processing'

The fax sender would of course use a different status (like 'fax_processing') so that the flagged records are isolated.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top