Conflict Resolution

How conflict resolution works.

When AppClient modifies objects (data) in the local cache in offline mode a situation might happen when the same objects are modified on the server-side too.

During the synchronization process, AppClient can't perform data modification operations that were made in offline mode automatically because in this case some important changes can be lost and data can be corrupted.

Such a situation is called a conflict.

AppClient offers conflict detection and resolving operations. You, the developer, should take conflict resolution into account as part of app design. We will show you examples of various conflict resolution scenarios to help you.

Example Scenario

Let's jump into an example.

There is a collection called Label in the Appery.io database with only one custom field: name (of type String).

A two-way syntonization service was generated and then imported into an app via API Express Extension as a generic service with the name label.

There is only one record (item1) in collection Label and the same record is available (saved) in the app (client). The following tables demonstrate the current client/server state:

Client History Client (9:00)ONLINE Server
operation _id old new
_id name _updatedAt
1 item1 9:00
_id name _updatedAt deleted
1 item1 9:00 false

🚧

Conflict Situation

A conflict can happen only when an app is offline mode.

The app (AppClient) switches to the offline mode.

The following tables demonstrate the current client/server state:

Client History Client (9:00) OFFLINE Server
operation _id old new
_id name _updatedAt
1 item1 9:00
_id name _updatedAt deleted
1 item1 9:00 false

Concurrent Update

Now you are going to see what happens if the client and server are updated to different values when they are disconnected (offline) from the server.

Client Update

At this point the app (AppClient) updates the record item1 stored in the app to item1_1. This update happens in the app (client).

Client History Client (9:00) OFFLINE Server
operation _id old new
UPDATE 1 item1 item1_1
_id name _updatedAt
1 item1_1 9:00
_id name _updatedAt deleted
1 item1 9:00 false

Server Update

On the server, item1 was updated to item1_2 at 9:10 but not from the mobile app but from from external system or an API, for example, an external web application. The following shows the current client/server state:

Client History Client (9:00) OFFLINE Server
operation _id old new
UPDATE 1 item1 item1_1
_id name _updatedAt
1 item1_1 9:00
_id name _updatedAt deleted
1 item1_2 9:10 false

Synchronization

At this point AppClient* moves from offline mode to online and starts the synchronization process. AppClient invokes the update operation (item1 > item1_1).

The server verifies the values of _updatedAt and old name from the client with the corresponding values on the server.

Since _updatedAt and old client-side name value (item1) don't equal the current values on server (item1_2), the server returns error response with correspondance error message and server value of the name field.

AppClient moves into the SYNC-FAILED state.

The following shows the current client/server state after the synchronization failed.

Client History Client (9:00) SYNC FAILED Server
operation _id old new
UPDATE 1 item1 item1_1
_id name _updatedAt
1 item1_1 9:00
_id name _updatedAt deleted
1 item1_2 9:10 false

AppClient returns a conflict object which contains all required information to solve a conflict:

  1. operation: UPDATE.
  2. old name value: item1.
  3. new name value: item1_1.
  4. server name value: item1_2.

Handling a Conflict

A conflict can be handled in the following ways:

  1. Conflict removal.
  2. Conflict update.
  3. Reverting changes.

Let's see an example of each approach.

Removing a Conflict

AppClient removes the conflict (removing the top record from Client History) and restores the old value (item1).

Client History Client (9:00) SYNC FAILED Server
operation _id old new
_id name _updatedAt
1 item1 9:00
_id name _updatedAt deleted
1 item1_2 9:10 false

After that AppClient reruns synchronization process and since Client History is now empty, AppClient switches into the online state.

Client History Client (9:00) ONLNE Server
operation _id old new
_id name _updatedAt
1 item1 9:00
_id name _updatedAt deleted
1 item1_2 9:10 false

AppClient invokes the FETCH operation, the data* on the server, and the client-side is synchronized and LastSyncDate is set to 9:10.

The following shows the client/server state:

Client History Client (9:10) ONLNE Server
operation _id old new
_id name _updatedAt
1 item1_2 9:10
_id name _updatedAt deleted
1 item1_2 9:10 false

This is one way to handle a conflict. Now let's look at another option.

Conflict Update

Now let's consider the app is back at the conflict state from the example above. This is the client/server state:

Client History Client (9:00) SYNC FAILED Server
operation _id old new
UPDATE 1 item1 item1_1
_id name _updatedAt
1 item1_1 9:00
_id name _updatedAt deleted
1 item1_2 9:10 false

AppClient updates the conflict object (update the top record from Client History to item1_3). The client data also will be updated. This is how it looks like:

Client History Client (9:00) SYNC FAILED Server
operation _id old new
UPDATE 1 item1 item1_3
_id name _updatedAt
1 item1_3 9:00
_id name _updatedAt deleted
1 item1_2 9:10 false

Now AppClient returns the SYNC operation and performs the UPDATE operation again:

Client History Client (9:20) ONLINE Server
operation _id old new
_id name _updatedAt
1 item1_3 9:20
_id name _updatedAt deleted
1 item1_3 9:20 false

The server considers the client value as newer and updates the data in the database.

AppClient switches to the online mode.

🚧

Invoking Retry Sync

If server value will be updated before invoking retry sync operation then server will be consider this situation again as a conflict. The user will have to resolve the conflict again and rerun the synchronization process.

Now AppClient switches to the offline mode.

Client History Client (9:20) OFFLINE Server
operation _id old new
_id name _updatedAt
1 item1_3 9:20
_id name _updatedAt deleted
1 item1_3 9:20 false

AppClient creates a new object with the name: item2.

Client History Client (9:20) OFFLINE Server
operation _id old new
CREATE t_1 - item2
_id name _updatedAt
1 item1_3 9:20
t_1 item2 -
_id name _updatedAt deleted
1 item1_3 9:20 false

AppClient updates item1_3 to item1_4.

Client History Client (9:20) OFFLINE Server
operation _id old new
CREATE t_1 - item2
UPDATE 1 item1_3 item1_4
_id name _updatedAt
1 item1_4 9:20
t_1 item2 -
_id name _updatedAt deleted
1 item1_3 9:20 false

AppClient removes item2.

Client History Client (9:20) OFFLINE Server
operation _id old new
CREATE t_1 - item2
UPDATE 1 item1_3 item1_4
DELETE t_1 item2 -
_id name _updatedAt
1 item1_4 9:20
_id name _updatedAt deleted
1 item1_3 9:20 false

Reverting Changes

Another option is to revert changes.

AppClient reverts all local (client) changes. All changes will be restored starting with the most recent one.

Revert DELETE operation

The following shows the client/server state when reverting the DELETE operation:

Client History Client (9:20) OFFLINE Server
operation _id old new
CREATE t_1 - item2
UPDATE 1 item1_3 item1_4
_id name _updatedAt
1 item1_4 9:20
t_1 item2 -
_id name _updatedAt deleted
1 item1_3 9:20 false

Revert UPDATE operation

The following shows the client/server state when reverting the UPDATE operation:

Client History Client (9:20) OFFLINE Server
operation _id old new
CREATE t_1 - item2
_id name _updatedAt
1 item1_3 9:20
t_1 item2 -
_id name _updatedAt deleted
1 item1_3 9:20 false

Revert CREATE operation

The following shows the client/server state when reverting the CREATE operation:

Client History Client (9:20) OFFLINE Server
operation _id old new
_id name _updatedAt
1 item1_3 9:20
_id name _updatedAt deleted
1 item1_3 9:20 false

Client History is empty and all local changes were reverted:

Client History Client (9:20) ONLINE Server
operation _id old new
_id name _updatedAt
1 item1_3 9:20
_id name _updatedAt deleted
1 item1_3 9:20 false

AppClient can invoke a reset operation and delete all information from Client History and from Client Database.