views:

52

answers:

2

In an app, Users and Cases have a many-to-many relationship. Users pull their list of Cases often, Users can update a single case at a time (a 1-10 second operation, requiring more than one UPDATE). Under READCOMMITTED, any in-use Case would block all associated Users from pulling their list of Cases. Also, the most recent data is a hotspot for both reads and writes to the Cases table.

I think I want to employ dirty reads to keep the experience snappy. READPAST on Cases won't work for this purpose. NOLOCK will work, but I'd like to be able to show which records are dirty when they are listed.

I don't know of any native way to show which records are dirty, so I'm thinking that for each update or insert to Cases, an INUSE flag will be set. This flag must be cleared by the end of the updating transaction such that under READCOMMITTED, this flag will never appear to be set. Note that this is NOT to replace concurrency management, only to show which records are potentially dirty to the User.

My question is whether this is reliable - if we UPDATE two or more fields (INUSE plus the other fields) in a single statement, is it possible that a concurrent NOLOCK query would read some of the new values but not others? If so, is it possible to guarantee that INUSE be set first?

And if I'm thinking about this all wrong, please enlighten me. My ideal situation would be to, in a manageable way, be able to show the values as they were PRIOR to any related transaction so the data is immediately available and always consistent (but partially out-dated). But I don't think this is available - especially in the more complex actual database.

Thanks!

+1  A: 

Restating the problem just to be sure: User A on connection A updates two columns (col1, col2) in MyTable. While this is going on, user B on connection B issues a dirty read, selecting data from that row. You are wondering if user B could get, say, the updated value in col1 AND the old/not updated value in col2. Correct?

I have to say: no way could this happen. As I understand it, updates are indeed an atomic transaction, and if you're writing data to the page (in memory), then the entire row update would have to finish on that set of bytes before anything else (another thread) could get access to them.

But I don't know for sure, and I can't imagine how to set up a test to confirm or deny this. The only answer I'd rely on would have to come from someone who actually had a hand in writing the code, or perhaps a Microsoft technician who has similar access. If you don't get any good answers here, posting the question on the appropriate MSDN forum (link) might get a good answer.

Philip Kelley
Thanks for responding. I up-voted you. Looks like the snapshot isolation might solve the problem.
uosɐſ
A: 

Have you considered using SNAPSHOT isolation level? When used for a query, it requires no locks whatsoever, and it gives precisely the semantics that you're asking for:

show the values as they were PRIOR to any related transaction so the data is immediately available and always consistent (but partially out-dated)

Pavel Minaev
Thanks! Wow, I'd seen snapshot isolation, but I thought it blocked everything so that transactions are actually serialized. I'm reading up on it now. What about phantom reads and such that are associated with dirty reads? Are these not a problem during inserts for snapshot isolation? (Guess not) Also, it is safe/possible to mix snapshot and readcommitted? I'd still like to use locking mechanisms of readcommitted for most operations, but when pulling the Users' Cases, I'd use snapshot.
uosɐſ
Snapshot is what the name says, so no dirty reads. It isn't quite the same as SERIALIZED (which blocks everything), because two concurrent modifying SNAPSHOT transactions can potentially conflict at the point of commit, so one of the will have to be rolled back. Yes, you can mix it with other isolation levels (though transactions of all levels will pay the versioning penalty for updates even when there's no active snapshot transactions).
Pavel Minaev
However, if you mix snapshot and locking, remember that snapshot still won't lock. I.e. snapshot will let you change rows that are locked by e.g. READ COMMITTED transaction. The changes will only be visible within the snapshot, and you'll get an error when you try to commit your snapshot transaction (you'll have to rollback). Between two READ COMMITTED transactions, it should just keep working normally. This seems to be a pretty good paper that covers a lot of things in detail in one go: http://msdn.microsoft.com/en-us/library/ms345124(SQL.90).aspx
Pavel Minaev