Uploaded image for project: 'AeroGear'
  1. AeroGear
  2. AEROGEAR-8284

Add ability to enable, detect and resolve data conflicts when working offline

XMLWordPrintable

    • Icon: Epic Epic
    • Resolution: Done
    • Icon: Major 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

            Unassigned Unassigned
            wtrocki@redhat.com Wojciech Trocki
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved: