Handling Conflicts

During synchronization, the destination provider receives a batch of changes from the source provider through the ProcessChangeBatch (for managed code) or ProcessChangeBatch (for unmanaged code) method. The destination provider must then detect and handle any conflicts that occur because of this list.

Conflict Detection

There are two categories of conflicts that can occur in synchronization: concurrency conflicts and constraint conflicts.

Concurrency Conflicts

Concurrency conflicts occur when the destination replica’s version of a change is not contained in the source replica's knowledge. These conflicts occur because operations such as update-delete and update-update affect the same item that is being synchronized.

Typically, the destination provider uses a change applier object that is provided by Sync Framework to detect concurrency conflicts. The change applier is represented by the NotifyingChangeApplier object (for managed code) or the ISynchronousNotifyingChangeApplier interface (for unmanaged code). To use a change applier to detect changes, the destination provider must provide version information for each item in the batch of changes sent from the source provider. The two ways to do this are as follows:

  • The destination provider builds a list of changes that corresponds to the batch of changes that are sent by the source provider. For each item in the source provider's change batch, the destination provider adds an item to its list that contains the version of this item on the destination replica. This list is passed to the change applier. The change applier uses it to verify that the destination version of each item is contained in the source replica's knowledge.

  • The destination provider does not provide a list of destination versions to the change applier. Instead, the destination provider implements the TryGetDestinationVersion (for managed code) or the ISynchronousNotifyingChangeApplierTarget::GetDestinationVersion (for unmanaged code) method. This method will be called one time for each item in the source provider's change batch.

To better understand what the change applier does to detect a conflict, consider the scenario in which a destination provider receives a change batch from a source provider. To detect conflicts, the following steps are performed for each item in the change batch:

  1. The destination provider determines whether the destination replica's version of the item is contained in the source replica's knowledge.

  2. If the destination replica's version is not contained in the source replica's knowledge, the object is said to be in conflict.

Constraint Conflicts

Constraint conflicts are conflicts that violate constraints that are put on items, such as the relationship of folders or the location of identically named data within a file system.

Because detecting a constraint conflict depends on the data store used by a replica, these types of conflicts must be detected by the provider. For example, a provider that represents a hierarchical file system must be able to detect store-specific constraints that are put on persisted data, such as location, naming, size, and so on.

Conflict Resolution

Conflicts can be resolved either by defining a conflict resolution policy that applies to all conflicts during the session, or by handling an event that occurs one time for each conflict.

Setting a Conflict Resolution Policy

To specify a policy that will be applied to all conflicts during a session, an application specifies a conflict resolution policy in the destination provider's ConflictResolutionPolicy property (for managed code) or in the ISyncSession::Start method (for unmanaged code).

Setting a Conflict Resolution Action

To set the conflict resolution action dynamically for each conflict that occurs, an application handles the item conflict event by using ItemConflicting (for managed code) or ISyncCallback::OnConflict (for unmanaged code). This event fires only when the conflict resolution policy is set to ApplicationDefined (for managed code) or CRP_NONE (for unmanaged code).

Managed code When ItemConflicting is raised, the event handler receives an ItemConflictingEventArgs object that contains metadata and item data for the two changes in conflict. The event handler can examine the two conflicts, make changes to the metadata or item data, and set the resolution action for the conflict by using the SetResolutionAction method. Sync Framework then processes the conflict and makes the appropriate calls to the source or destination provider to apply any changes.

Unmanaged code When ISyncCallback::OnConflict is raised, the event handler receives an IChangeConflict object that contains metadata and item data for the two changes in conflict. The event handler can examine the two conflicts, make changes to the metadata or item data, and set the resolution action for the conflict by using the IChangeConflict::SetResolveActionForChange method. Sync Framework then processes the conflict and makes the appropriate calls to the source or destination provider to apply any changes.

Conflict Resolutions

Sync Framework provides the following set of conflict resolution actions for which it handles most of the processing. Other types of conflict resolution can be performed by working with the conflicting changes directly in the item conflict event handler.

Conflict resolution Description Available as session policy or item action?

Source wins

The source replica always wins. This supports a read-only synchronization solution in which the destination replica is not to be trusted. The change applier passes the change to the SaveItemChange (for managed code) or ISynchronousNotifyingChangeApplierTarget::SaveChange (for unmanaged code) method. The change is applied to the destination replica exactly like any non-conflicting change.

Both

Destination wins

The destination replica always wins. This supports the case in which the destination replica does not consume changes that are made by remote clients. The change applier passes the conflicting change to the SaveItemChange (for managed code) or ISynchronousNotifyingChangeApplierTarget::SaveChange (for unmanaged code) method as a version-only change. Only version information is applied to the metadata on the destination replica.

Both

Merge

Merge the information from one item into the other. The change applier passes the conflicting change to the SaveItemChange (for managed code) or ISynchronousNotifyingChangeApplierTarget::SaveChange (for unmanaged code) method as a merge change. Source item data and destination item data are merged and the result is applied to the destination replica.

Item action only

Log

Log the conflict to handle later and do not apply the change. The change applier passes the conflicting change to the SaveConflict (for managed code) or ISynchronousNotifyingChangeApplierTarget::SaveConflict (for unmanaged code) method.

Item action only

Defer

Ignore the conflict and do not apply the change. The change applier does not pass the conflicting change to the destination provider.

Item action only

Saved Conflicts

When a conflict is logged, the SaveConflict (for managed code) or SaveConflict (for unmanaged code) method is called instead of the SaveItemChange (for managed code) or SaveChange (for unmanaged code) method. In this case, the provider must save the conflict knowledge, the conflicting change, and the conflict data. An application can then enumerate these conflicts from the conflict log and resolve them later. Be aware that this kind of conflict resolution is always a local change. Therefore, the application that is resolving conflicts must apply the resolution as a local change, update the tick count, and add the conflict knowledge to the local knowledge.

Additionally, an application that is resolving saved conflicts must handle obsolete conflicts. In particular, when a provider logs a new conflict, it must check the new conflict against its knowledge to ensure that the conflict that is being logged supersedes any previously logged conflicts for that item. Obsolete conflicts must also be cleaned up from the log. An application can clean up obsolete conflicts asynchronously instead of in the provider. In this case, the application must verify that conflicts are not obsolete before it tries to resolve them.

Reducing Conflicts by Using Change Units

The number of conflicts can be reduced by using change units to represent subitem changes. When change units are used, versions are tracked for change units instead of for the item as a whole. Therefore, changes that are made to different change units within the same item will not result in a conflict. For more information, see Synchronizing Change Units.

See Also

Reference

ISyncKnowledge Interface
ISyncProvider Interface
IKnowledgeSyncProvider Interface
IKnowledgeSyncProvider::ProcessChangeBatch
ISynchronousNotifyingChangeApplierTarget Interface
IAsynchronousNotifyingChangeApplierTarget Interface
ISyncCallback::OnConflict
CONFLICT_RESOLUTION_POLICY Enumeration
SYNC_RESOLVE_ACTION Enumeration
SyncKnowledge
KnowledgeSyncProvider
INotifyingChangeApplierTarget
ItemConflicting
ConflictResolutionPolicy
ConflictResolutionAction

Concepts

Synchronization Providers
Applying Changes
Understanding Synchronization Knowledge