同步框架 - 冲突解决触发更改,导致不必要的下载

时间:2014-07-30 18:58:22

标签: microsoft-sync-framework

我正在使用在集线器中配置的Sync Framework v2.1< - >说话时尚。 Hub:使用SqlSyncProvider的SQL Server 2012。 Spokes:使用SqlSyncProvider的LocalDb 2012。每个辐条'数据库从服务器开始作为已还原的备份,然后对其执行PostRestoreFixup。在调查此问题时,我还尝试从一个空的辐条数据库开始,该数据库的模式和数据是通过配置和初始的仅下载同步创建的。

假设两个辐条(A& B)和一个中央集线器(让我们称之为H)。它们每个都有一个包含一条记录的表,它们都是同步的。

  1. Spoke A更改记录和同步,留下A& H记录相同。
  2. Spoke B更改相同的记录并进行同步,从而导致与步骤1中所做的更改发生冲突。 B的记录被H覆盖,H的记录保持原样。这是预期/期望的结果。但是,协调器返回的SyncOperationStatistics建议在H处进行更改。我已经尝试了两个SyncDirectionOrder方向,这些结果如下:

    - DownloadAndUpload (H's local_update_peer_timestamp and last_change_datetime are updated) -->
        * Download changes total: 1
        * Download changes applied: 1
        * Download changed failed: 0
        * Upload changes total: 1
        * Upload changes applied: 1
        * Upload changed failed: 0
    
    - UploadAndDownload (H's local_update_peer_timestamp is updated)-->
        * Upload changes total: 1
        * Upload changes applied: 1
        * Upload changed failed: 0
        * Download changes total: 1
        * Download changes applied: 1
        * Download changed failed: 0
    
  3. 事实上,当Spoke A再次同步时,记录会从H下载,即使H的记录没有改变。为什么呢?

  4. 由此产生的问题是,例如,如果Spoke A对步骤#2和3之间的记录进行了另一次更改,则该更改将(错误地)标记为冲突,并将在步骤#3中被覆盖。 / p>

    这里有一些简化的代码来证明这个问题,或者说是我的问题。请注意,我已经实现了提供程序的ApplyChangeFailed处理程序,以便服务器获胜,无论SyncDirectionOrder如何:

    private const string ScopeName = "TestScope";
    private const string TestTable = "TestTable";
    
    public static SyncOperationStatistics Synchronize(SyncEndpoint local,SyncEndpoint remote, EventHandler<DbSyncProgressEventArgs> eventHandler)
    {
        using (var localConn = new SqlConnection(local.ConnectionString))
        using (var remoteConn = new SqlConnection(remote.ConnectionString))
        {
            // provision the remote server if necessary
            //
            var serverProvision = new SqlSyncScopeProvisioning(remoteConn);
            if (!serverProvision.ScopeExists(ScopeName))
            {
                var serverScopeDesc = new DbSyncScopeDescription(ScopeName);
                var serverTableDesc = SqlSyncDescriptionBuilder.GetDescriptionForTable(TestTable, remoteConn);
                serverScopeDesc.Tables.Add(serverTableDesc);
                serverProvision.PopulateFromScopeDescription(serverScopeDesc);
                serverProvision.Apply();
            }
    
            // provision locally (localDb), if necessary, bringing down the server's scope
            //
            var clientProvision = new SqlSyncScopeProvisioning(localConn);
            if (!clientProvision.ScopeExists(ScopeName))
            {
                var scopeDesc = SqlSyncDescriptionBuilder.GetDescriptionForScope(ScopeName, remoteConn);
                clientProvision.PopulateFromScopeDescription(scopeDesc);
                clientProvision.Apply();
            }
    
            // create\initialize the sync providers and go for it...
            //
            using (var localProvider = new SqlSyncProvider(ScopeName, localConn))
            using (var remoteProvider = new SqlSyncProvider(ScopeName, remoteConn))
            {
                localProvider.SyncProviderPosition = SyncProviderPosition.Local;
                localProvider.SyncProgress += eventHandler;
                localProvider.ApplyChangeFailed += LocalProviderOnApplyChangeFailed;
    
                remoteProvider.SyncProviderPosition = SyncProviderPosition.Remote;
                remoteProvider.SyncProgress += eventHandler;
                remoteProvider.ApplyChangeFailed += RemoteProviderOnApplyChangeFailed;
    
                var syncOrchestrator = new SyncOrchestrator
                {
                    LocalProvider = localProvider,
                    RemoteProvider = remoteProvider,
                    Direction = SyncDirectionOrder.UploadAndDownload // also issue with DownloadAndUpload
                };
    
                return syncOrchestrator.Synchronize();
            }
        }
    }
    
    private static void RemoteProviderOnApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e)
    {
        // ignore conflicts at the server
        //
        e.Action = ApplyAction.Continue;
    }
    
    private static void LocalProviderOnApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e)
    {
        // server wins, force write at each client
        //
        e.Action = ApplyAction.RetryWithForceWrite;
    }
    

    重申一下,沿着开头描述的配置使用此代码,正如预期的那样,在包含冲突的辐条上覆盖冲突的行,并且该行的服务器版本保持原样(未更改) )。但是,我发现每次冲突都会导致服务器的xxx_tracking表更新,特别是local_update_peer_timestamp和last_change_datetime字段。我猜测,即使服务器的数据没有真正改变,也会导致下载到其他所有语音。这似乎是不必要的,对我来说,这是违反直觉的。

0 个答案:

没有答案