从 WCF 客户端传播到 WCF 服务的事务的隔离是啥意思?

Posted

技术标签:

【中文标题】从 WCF 客户端传播到 WCF 服务的事务的隔离是啥意思?【英文标题】:What is the meaning of Isolation for a transaction that propagates from WCF client to WCF service?从 WCF 客户端传播到 WCF 服务的事务的隔离是什么意思? 【发布时间】:2018-11-25 04:06:34 【问题描述】:

问题

这个问题分为三个部分:

    为什么 Serializable 事务不能以原子方式执行操作? 假设答案是事务的原子性并不能保证其组成操作的原子性(并且它只确保所有操作要么全部成功或全部失败),那么为什么事务的隔离要求不能确保操作是原子的?我读过 Serializable 隔离级别确保事务的执行就像它们是串行执行的一样? 如果我对 Isolation 的解释不正确,那么正确的解释是什么?如何修改测试以证明使用序列化事务与根本不使用事务之间的区别。

一个最小的完整和可验证的例子

代码可以从here下载

假设 DataLayer (DAL) 由 WCF 服务实现,并且客户端代码包含从 Main 对其操作的调用:

    public void Main(string[] args)
    
        var dal = new DataLayerServiceClient();

        var accounts = dal.GetAccounts();
        int accountId = accounts.First().AccountId;

        for (int i = 0; i < 10000; i++)
        
            using (TransactionScope scope = new TransactionScope())
            
                var account = dal.GetAccountById(accountId);
                account.Balance++;
                dal.Update(account);

                scope.Complete();
            
        
    

假设:

    客户端和服务已正确配置以将客户端事务传播到服务。 (通过观察存在环境事务,它具有分布式标识符并且该标识符与客户端上的标识符相同,在服务端对此进行了验证。 事务的隔离模式(在服务和客户端)是可序列化的(通过观察服务和客户端环境事务的属性来验证)

测试说明

同时运行两个客户端进程。

预期结果

预期结果是两个客户端退出后的账户余额应该比两个客户端启动前的余额大20000。

实际结果

两个客户端退出后的账户余额介于 10000 和 20000 之间。在某些情况下,其中一个客户端由于以下错误而中止:

事务(进程 ID)在锁定资源上与另一个死锁 进程并被选为死锁受害者

结论

每个客户端上包含在 TransactionScope 范围内的操作并未作为一个整体与其他客户端的操作串联运行。 来自两个事务的读取和写入是混合的,并且一些增量丢失了。

【问题讨论】:

@bommelding 我已经编辑了帖子以包含指向完整代码的链接。

@mjwills 你对更新的看法是正确的。这是更新的服务端代码:command.CommandText = "UPDATE Accounts SET name = @Name, Balance = @Balance WHERE AccountId = @AccountId";
使用可序列化的 tx,您的 WHERE 中不需要并发列。 让我们分解一下:当您在客户端范围内仅执行 2 次调用时,您会在中断它时获得预期的行为吗? @bommelding 你能解释一下为什么这个例子会导致死锁吗? 啊,你原来的问题不清楚。您声明了The balance of the account after both clients exit is a value between 10000 and 20000. In some cases, one of the client is aborted due to the following error:,但不清楚这两件事是否相关(即 【参考方案1】:
    问题:“为什么 Serializable 事务不能以原子方式执行操作”答案:Serializable 事务是原子的,因为它的所有操作要么全部成功,要么全部成功失败。这个例子没有反驳。 问题:“为什么事务的隔离要求不能确保操作是原子的?答案:两个可序列化事务满足隔离要求,因为它们不会并发运行。如果两个尝试一起运行,一个将按计划继续运行,另一个中止。这正是问题中报告的异常发生的原因。(“事务(进程 ID)与另一个进程在锁定资源上死锁并被选为死锁受害者”)。

    问题:“如果我对隔离的解释不正确,那么正确的解释是什么?”。 答案:问题中的错误假设是,如果两个事务不能同时运行,一个事务将等待另一个事务完成然后继续。这是不正确的。事实上,可序列化事务是隔离的,不会同时运行,但这并不意味着一个会等待另一个。隔离事务与持有互斥锁以隔离地执行一组操作是不一样的。问题:“我怎样才能修改测试以证明使用的区别与根本不使用事务相反的序列化事务”答案:以下 sn-p 将演示使用事务来确保按预期发生增量。

        int i = 0;
        while(i < 10000)
        
            try
            
                using (TransactionScope scope = new TransactionScope())
                
                    var account = dal.GetAccountById(accountId);
                    account.Balance++;
                    dal.Update(account);
    
                    scope.Complete();
                
                i++;
            
            catch (Exception ex)
            
                Console.WriteLine($"ex.Message : restarting");
            
        
    

当然,这是非常低效的,但它可以按预期工作,并演示事务如何隔离资源管理器上的操作。

【讨论】:

以上是关于从 WCF 客户端传播到 WCF 服务的事务的隔离是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章

Wcf 事务和 SQL 存储过程

WCF 可靠性问题

WCF 服务中的模拟和传播到 COM 对象

从 Silverlight 到 WCF 的图像文件

无法从客户端成功发布到 WCF WSDL 服务

从 WCF 服务向网站发送数据