对于从WCF客户端传播到WCF服务的事务,隔离的含义是什么?

时间:2018-06-15 10:08:31

标签: c# wcf transactions acid

问题

这个问题有三个部分:

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

    可以从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();
                }
            }
        }
    

    也假设:

    1. 正确配置客户端和服务以将客户端事务传播到服务。 (这在服务方面通过观察存在环境事务来验证,它具有分布式标识符,并且标识符与客户端上的标识符相同。
    2. 事务的隔离模式(在服务和客户端)都是Serializable(通过观察服务和客户端上的环境事务的属性来验证)
    3. 测试说明

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

      预期结果

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

      实际结果

      两个客户端退出后的帐户余额是10000到20000之间的值。在某些情况下,由于以下错误,其中一个客户端被中止:

        

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

      结论

      每个客户端上TransactionScope范围内包含的操作不会与其他客户端的系列整体运行。 两个事务的读取和写入都是混合的,一些增量丢失了。

1 个答案:

答案 0 :(得分:0)

  1. 问题:“为什么Serializable事务不会以原子方式执行操作” 答案:Serializable事务是原子的,因为它的所有操作都将成功或全部失败。这个例子没有反驳。
  2. 问题:“为什么事务的隔离要求不能确保操作是原子的?
    答案:两个Serializable事务符合隔离要求,因为它们不会同时运行。如果两个尝试一起运行,一个将按计划继续,另一个将中止。这正是问题中报告的异常发生的原因。 (“事务(进程ID)在锁资源上与另一个进程死锁,并被选为死锁牺牲品”)。
  3. 问题:“如果我对隔离的解释不正确,那么正确的解释是什么?”。
    答案:问题中的错误假设是,如果两个事务无法同时运行,则会等待另一个事务完成然后继续。这是不正确的。事实上,Serializable事务是孤立的,不会同时运行,但这并不意味着一个人会等待另一个。事务的隔离与持有互斥锁以便单独执行一组操作不同 问题:“我怎样才能修改测试以证明使用序列化交易与完全不使用交易相反的区别”
    回答:以下代码段将演示如何使用事务来确保按预期进行增量。

        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");
            }
        }
    
  4. 当然这是非常低效的,但它按预期工作,并演示了事务如何隔离资源管理器上的操作。