Logging and Managing Conflicts
During synchronization, a synchronization application might indicate that a conflict must be saved in a conflict log, instead of being resolved immediately. By using a conflict log, conflicts can then be resolved separately from synchronization, so that synchronization can complete as efficiently as possible.
Resolving a Conflict by Logging
To indicate that a concurrency conflict is to be saved, the application sets a conflict resolution action of SaveConflict (for managed code) or SRA_TRANSFER_AND_DEFER (for unmanaged code). To indicate that a constraint conflict is to be saved, the application sets a conflict resolution action of SaveConflict (for managed code) or SCRA_TRANSFER_AND_DEFER (for unmanaged code).
Logging a Conflict
To log a concurrency conflict, the change applier calls SaveConflict(ItemChange, Object, SyncKnowledge) (for managed code) or ISynchronousNotifyingChangeApplierTarget::SaveConflict (for unmanaged code). In this method, the provider saves the metadata of the conflicting change, the data of the conflicting change, and the specified conflict knowledge.
To log a constraint conflict, the change applier calls SaveConstraintConflict(ItemChange, SyncId, ConstraintConflictReason, Object, SyncKnowledge, Boolean) (for managed code) or ISynchronousNotifyingChangeApplierTarget2::SaveConstraintConflict (for unmanaged code). In this method, the provider saves the metadata of the conflicting change, the data of the conflicting change, the ID of the conflicting item in the destination replica, the reason for the conflict, and the specified conflict knowledge.
Before a conflict is stored in the conflict log, the provider must ensure that the conflict is not superseded by another change that is already in the conflict log. One way to accomplish this is to create a log knowledge by using the Combine(SyncKnowledge) method (for managed code) or ISyncKnowledge::Union method (for unmanaged code) to combine the conflict knowledge of all of the conflicts in the log. If the new conflict is contained in the log knowledge, it is obsolete and should not be added to the log. This log knowledge can be created on demand or can be stored along with the conflict log. If the log knowledge is stored, it must be updated when a conflict is added, by using Combine(SyncKnowledge) (for managed code) or Union (for unmanaged code). Similarly, when a conflict is removed, the log knowledge must be updated by using Complement(SyncKnowledge) (for managed code) or ISyncKnowledge2::Complement (for unmanaged code).
Removing Obsolete Conflicts
To prevent the conflict log from growing too large, obsolete conflicts must be removed from the conflict log.
Detection and removal of obsolete conflicts is most easily accomplished by using the change applier component of Sync Framework. To connect a conflict log to the change applier, the provider or conflict log implements the IConflictLogAccess and IConflictLogWriter interfaces (for managed code) or the IConflictLogAccess and IConflictLogWriter interfaces (for unmanaged code). The conflict log is passed to the change applier by using the
ApplyChanges(ConflictResolutionPolicy, CollisionConflictResolutionPolicy, ChangeBatch, IChangeDataRetriever, SyncKnowledge, ForgottenKnowledge, INotifyingChangeApplierTarget, IConflictLogAccess, SyncSessionContext, SyncCallbacks) method (for managed code) or ISynchronousNotifyingChangeApplier2::ApplyChanges method (for unmanaged code). When a conflict log is passed to the change applier, the change applier detects and removes obsolete conflicts that are in the log at the end of each change batch.
For a conflict log that does not connect to a change applier, the conflict log must manage obsolete conflicts itself. A conflict can become obsolete when a conflict is added to the conflict log that supersedes a conflict that is in the log. One way to detect this case is to test the change version of each conflict in the log against the conflict knowledge of the new conflict. When the change version of a conflict is contained in the conflict knowledge of the new conflict, the existing conflict is obsolete and must be removed from the log. A conflict can also become obsolete when a change is applied to the replica that supersedes a conflict that is in the log. One way to detect this case is to test the change version of each conflict in the log against the knowledge of the replica after each change batch is applied. Detection and resolution of obsolete conflicts can be performed either as part of the synchronization session or at another time.
Resolving Conflicts in the Log
Conflicts in the conflict log can be resolved either as part of the synchronization session or at another time. However, conflicts in the conflict log should not be resolved during change application, or unexpected results may occur.
When a conflict in is log is applied to the replica, it must be treated as a local change. To resolve a conflict in the conflict log, the application or provider performs the following steps:
Increments the tick count of the replica.
Updates the change version of the item or change unit to reflect that the change is made by the local replica with the updated tick count.
Combines the knowledge of the replica with the knowledge of the conflict by using Combine(SyncKnowledge) (for managed code) or Union (for unmanaged code), and saves the combined knowledge as the new knowledge of the replica.
Applies the change data to the replica.
Removes the conflict from the conflict log.
In-memory Conflict Log
A conflict log is required when a provider reports constraint conflicts. To help a provider that reports constraint conflicts for a replica that does not contain a conflict log, Sync Framework provides an implementation of the conflict log interfaces that operates in memory and is used to store temporary conflicts that may arise as a result of constraint conflict handling. The implementation is the MemoryConflictLog class (for managed code) or the IMemoryConflictLog interface, which can be obtained by calling IProviderSyncServices2::CreateMemoryConflictLog (for unmanaged code).
The in-memory conflict log implementation can also be used in conjunction with a persistent conflict log. The in-memory conflict log can be used to gain performance improvements by using it to provide an in-memory cache of the full conflict log and to handle temporary conflicts. When a persistent conflict log is chained to the in-memory conflict log, conflicts can be save to the persistent conflict log after synchronization completes by calling [M:Microsoft.Synchronization.MemoryConflictLog.Persist()] (for managed code) or IMemoryConflictLog::Persist (for unmanaged code).