-
Epic
-
Resolution: Done
-
Major
-
None
-
None
-
None
-
OfflineConflicts
-
To Do
High level targets
Enable framework for effective conflict resolution on client and server.
Enable full traceability for conflict resolution
Allow developers to define what conflict means and how it's detected
Build sample application that (integrate with current sample application
Non target
- Provide comprehensive query mechanism working with conflict resolution.
- Provide data focused conflict resolution (data from different sources)
Technical information
Enablement
Server side is dictatic conflict resolution policies. Users can provide default state provider in the configuration:
{
conflictProvider?: new VersionedObjectState()
}
By default conflict provider is `VersionedObjectState` which implements `version` field conflict detection.
Top level overview
Users interact with the conflict interface by calling hasConflict and next methods. They can import one of their predefined strategies or write their own. Conflict resolution is applied per resolver basis which gives great customization options.
Operating on Object State on server
To effectively manage conflict resolution we need to have:
- effective way to iterate on object state (hashing, changing version)
- effective way to detect difference between version (collision aka conflict)
This can be achieved by following interface.
export interface ObjectState {
/**
*
* @param serverData the data currently on the server
* @param clientData the data the client wishes to perform some mutation with
*/
detectConflict (serverData: ConflictResolutionData, clientData: ConflictResolutionData): ConflictInfo
/**
*
* @param currentObjectState the object wish you would like to progress to its next state
*/
next (currentObjectState: ConflictResolutionData): ConflictResolutionData
}
Conflict resolution
Conflict resolution will use following interface:
(currentRecord: ConflictResolutionData, client: ConflictResolutionData) => ConflictResolutionData
Communication between server and client
Server side will always return GraphQL error when conflict is detected thru interface.
Error will contain information about:
- Client and server state
- If conflict was resolved on server or deferred to client.
Conflict resolution listeners on client
Client UI should be notified about the conflict being resolved.
Conflict resolution and error strategy
Mutations error strategy needs to be specified in docs. Strategies like `none` cannot be employed since users will not be able to see results coming back from server. Alternatively we should map server side error to different GraphQL error but this will mean that we will lose ability to notify users about conflict error for specific object.
Conflict resolution and orphaned objects
Retrying policies should be employed on the ConflictLink to prevent from endless loop.
We should track conflicted data so there is no objects left in cache
Special cases conflicts
Delete operations trigger conflicts that are not easily solvable using predefined strategies (due to fact that delete actually removes data). We need to apply strategy to clear the cache inside the link if delete happens.
Subscription support
Conflicted data should not trigger subscriptions until data will be actually saved to the database.
Implementation details for this matter rely strictly on users performing publish subscribe operations, however we need to clearly document that as subscriptions can happen only when conflict is being resolved.
Error handling
To properly handle errors we may need to port retry error logic and make it work with conflict resolution.
Implementation details
Versioning is already done in example app. All improvements should be done on JS-SDK level
Data access methods are already defined and working.
Basic conflict resolution link is currently added to SDK:
https://github.com/aerogear/aerogear-js-sdk/tree/master/packages/sync/src/conflicts
Implementation ported from spike repo to server, however it may need to be changed to follow up new aproach:
https://github.com/aerogear/apollo-voyager-server/pull/15
Packaging and deployment
Both client side and server will have different config and internal processing embedded into them.
If we identify some common or shared interface we may consider reusing server side package in client