2

Assume that houses have occupants and that no two occupants of any house can have the same height.

  1. Pick a random house
  2. Get a list of the current occupants of the house
  3. Decide which ones to keep, replace, evict, or add by examining the list
  4. Make sure that the new list doesn't contain any occupants of the same height.
  5. Replace the existing list with the new list; delete, insert, or update as required.

Sounds simple enough but when you have 50 threads all trying to do this at the same it gets complicated. I used UPDLOCK, ROWLOCK on the select (step 2) to block possible updates to any of the occupants I might want to update. I get an occasional failure, however, when there are no current occupants and a new occupant is added. The failure is always a unique constraint violation. This should never happen (see step 4) but it does.

Steps 2-5 are being performed within a TransactionScope using ReadCommitted isolation level.

Is there a best practices model somewhere that defines how a scenario like this should be handled?

2 Answers2

0

Take a look at the transaction isolation levels in SQL Server. It sounds like your problem is likely that you're having phantom reads (ie, somebody is inserting data into a range that you've already SELECT'ed).

With a high-level of concurrency in a situation like this, I'd definitely recommend that you run all of the associated queries (ie, SELECT followed by appropriate INSERT/UPDATE/DELETE's) in a single transaction with the isolation level set to Serializable. That way, SQL Server will ensure that the transactions happen in complete isolation from each other.

However, the best solution in this case, given its high degree of concurrency, is probably going to be to implement some sort of locking/synchronization in your code, rather than only relying on SQL Server to do it for you. (Although, I'd still recommend using the Serializable isolation level.)

For example, use some sort of lock-manager that generates a lock-object for each occupant and house in such a way that you'll always get the same object for the same occupant (or house), and have each thread use that object for entering a critical section (or something similar C#'s Monitor.Enter) - just be careful to avoid deadlocks. That way, you guarantee that only one thread is examining any particular house or any particular occupant at any given time, but still allows other threads to run.

Ryan
  • 2,948
  • 3
  • 30
  • 41
0

I've realized that this isn't a SQL or TransactionScope related issue. I am reconciling a collection of data items and access to the collection must be semaphored while that's happening. In my case, I just needed to add a lock(List) { around the code that does the reconciliation. }

To continue the analogy, if you're going to reconcile a list of occupants, it's probably a good idea to lock the doors to prevent occupants from getting in or out while you're doing it.