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.

Conflict
Types
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.
To allow you to
generate these
conflicts
without a lot of
hassle, I added
a button for
each conflict
type in the main
form as show in
the snapshot
above.
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.
To implement
'force write'
functionality
for the
update-update
conflict, I
changed the
update stored
procedure from
the previous
demo to the
following:
create
procedure
dbo.sp_orders_applyupdate
(
@sync_last_received_anchor
binary(8)
,
@sync_client_id_hash
int
,
@sync_force_write
int,
@sync_rowcount
int out,
@order_id
int,
@order_date
datetime
=
NULL
)
as
update
[orders]
set
[order_date] =
@order_date,
[update_originator_id]
= @sync_client_id_hash
where
(update_timestamp
<= @sync_last_received_anchor
or
update_originator_id
=
@sync_client_id_hash)
and [order_id]
= @order_id
set
@sync_rowcount
= @@rowcount
-- force
write resolution option
if
@sync_rowcount
= 0 and
@sync_force_write
= 1
begin
update [orders]
set
[order_date] = @order_date,
[update_originator_id]
= @sync_client_id_hash
where
[order_id]
= @order_id
set
@sync_rowcount
= @@rowcount
end
go
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.
Writing these
two command is
very direct as
show in the code
below:
create
procedure
dbo.sp_orders_getupdateconflict
@order_id
int
as
-- this
command finds the conflicting row in the
base table for [orders]
select order_id,
order_date from
orders where
order_id =
@order_id
go
create
procedure
dbo.sp_orders_getdeleteconflict
@order_id
int
as
-- this
command finds the conflicting row in the
tombstone table for [orders]
select order_id
from
orders_tombstone
where
order_id =
@order_id
go
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
Version
1.5 [C#]:
Download Now
|