It is about time for our offline application to deal with sync conflicts. Conflicts are fact of life in lazy synchronization environments. It is likely that your application users don’t want to deal with conflicts. Therefore you are encouraged to architect your solution in such a way that would make conflicts rare. However, your application is not complete without conflict handling logic. Before I go into more details, let’s define what conflict is:
“A row is said to be in conflict if it was changed on two or more nodes between synchronization requests”
In two node environment, client could make a change to a row R while offline, at the same time a change is made at the server to R. When both ends sync, a conflict is detected. In n-node environment, a conflict could happen by independent changes to R on two or more clients.
The sync framework detects four types of conflicts:
- ClientInsertServerInsert also known as PK Collision; occurs when both client and server insert a row with same PK
- ClientUpdateServerUpdate the most common conflict; occurs when client and server make changes to the same row independently
- ClientUpdateServerDelete occurs when the client updates a row and the server independently deletes the same row
- ClientDeleteServerUpdate occurs when the client deletes a row and the server independently updates the same row
Note: The use of “Server” term in the conflict type names above does not necessarily mean that the changes were authored on the server. In fact, the server could be a passive node that just collects and distributes changes. However, from the synchronizing client point of view, the change appears to have happened on the server thus the naming.
Conflict Detection and Resolution
The sync framework fires ApplyChangeFailedEvent from both client and server sync providers when it detects a conflict. In this demo application, I registered to receive conflict events from both client and server providers. Each conflict is then presented to the user with possible conflict resolution options:
- Ignore the conflict and continue
- Retry applying the row, without changing to the datarow the retry will fail causing the event to be raised again. The demo does not allow you to edit the datarow though.
- Retry with Force Write has a built in support in the ClientSyncProvider, on the server side, you need to add support in your sync adapter commands and the demo show an example for update-update conflict.
- Abort the Sync by throwing an exception; all changes will be rolled back and re-synced in the next sync session.
SyncAdapter Conflict Commands
There are two commands that we did not explore in the previous demos:
- SelectConflictUpdatedRowsCommand this command finds conflicting rows in the base table. The sync runtime executes this command if insert, update or delete command failed (i.e. returned 0 row count)
- SelectConflictDeletedRowsCommand this command finds conflicting rows in the tombstone table. The sync runtime executes this command if the conflicting row was not found in the base table. This is how ClientUpdateServerDelete conflict is detected.
- I’ve added few more buttons to generate random conflicts
- Also I changed the tombstone tables to accept more than one delete for the same PK. (there were questions about that in the forum)
How to install OfflineAppDemo application?
- Fire SQL server and load demo.sql file
- Execute the script untill the "test sample" marker
- Load server_procs.sql file and execute it to install server procs
- Load VS solution (OfflineAppDemo-Conflicts Project)
- Build the project
- You are ready to go