Distributed transaction with MSMQ and SQL Server but sometimes getting dirty reads

為{幸葍}努か 提交于 2019-12-12 18:36:07

问题


Our SQL Server 2014 database is set to READ_COMMITTED_SNAPSHOT.

We use MSMQ and distributed transactions (we use MassTransit 2.10)

In one part of our system we read a message from the queue, make database updates and then publish a new message to the queue (all under a single transaction).

We have found a situation where it seems that the update are not committed when the next message is processed (it reads from the same table the first part updates) even though I would expect that message to only be on the queue at the same time the database is updated. When we query the table later the updated data is there as expected. This only seems to happen when we have high load and very rarely.

Simplified version of our code

// code that processes message 1
using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions() { Timeout = TimeSpan.FromMinutes(30), IsolationLevel =  IsolationLevel.ReadCommitted }) 
{
     MethodThatUpdatesTableX();
     MethodThatCreatesMessage2();
     scope.Complete();
}    

// message picked up from MSMQ and then (this runs in different thread):
// code that process message 2
using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew,  new TransactionOptions() { Timeout = TimeSpan.FromMinutes(30), IsolationLevel = IsolationLevel.ReadCommitted }) 
{
     MethodThatReadsFromTableX(); // so here it seems that changes made in MethodThatUpdatesTableX is sometimes (though rarely) not read
    // other stuff
}    

So here is my understanding:

When the scope is disposed the changes to table X is committed as well as the message published to the queue

When MethodThatReadsFromTableX() reads from table X I would expect the changes to be there (the session should not be created before the first one is completed because it wouldn’t be able to pick up the message from the queue)

Is my expectation correct? What could the issue be?


回答1:


I'll give you a really, really short answer.

Use Serializable as your isolation level. It's the only way to guarantee that readers will be blocked by writers.

The rest...

SQL is very good at row-level locking, unless you are inserting sequentially into a clustered index. In that case, there are performance benefits to inserting and handling the duplicate key error -- an order of magnitude benefit.

You also need to make sure that your readers that might be getting dirty reads are also reading using serializable -- otherwise, you're going to have a case where a published message is being consumed before the database transaction is committed. I've seen this in high volume production systems, and it happens (of course, with RabbitMQ not MSMQ - RabbitMQ dispatches much quicker than MSMQ).




回答2:


The following is my thoughts about this problem.

In distributed transaction, two-phase commit is usually used. In first phase, transaciton coordinator asks everyone to prepare to commit. If everyone agrees, in phase two coordinator will command everyone to commit changes. Note that when node has commited changes during this phase - it will not rollback any more. Also note that there might be inevitable gap between participant A (for example Sql server) and participant B (MSMQ) actually commits their changes. When MSMQ already commited changes, it might be that SQL Server did not yet (though has already been commanded to).

So, when MSMQ commits transaction, nothing prevents created message to be delivered to your processor, which as you said runs in another thread. So even before you leave first "scope" block - your processor might start processing received message, and SQL server might still not yet commited changes. Distributed transaction cannot prevent such behavior, because it cannot somehow force every participant to finish committing their changes in exactly the same time.

Long story short - I think it works as intended and you should be prepared that required data can be missing when you handle your MSMQ message, and implement retrying logic.



来源:https://stackoverflow.com/questions/37463562/distributed-transaction-with-msmq-and-sql-server-but-sometimes-getting-dirty-rea

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!