Conflict resolution for element updates #17

Open
opened 2024-02-13 16:20:48 +00:00 by philip · 0 comments
Owner

This is a major feature and very complex. Some thoughts:

  • Conflict resolution strategies ubisync should be as open to different conflict resolution strategies as possible, but since all nodes sharing an element need to be able to interpret it. As a start, every Element could have exactly one permanent assigned strategy. Changing the strategy would require creating a new element and either recreating the change history or just using the latest "snapshot" (whatever "latest" may mean in that sense). The strategy would be stored in a member variable of Element.
    It should be possible for an app to handle conflict resolution itself, because there may be very domain-specific ways to handle it. In that case, the ContentUpdateStrategy is basically a no-op, passing the message on to the app.
  • Default strategies The global default will most likely be a plain overwrite strategy which replaces the content of an element by any recently received new version of the content. However, for some ElementContent variants there may be better strategies which are always applicable. There should be a way to map any ElementContent to its default optimal strategy, in case the app does not specify one, to avoid sub-optimal inconsistent overwrite wherever possible.
  • Compatibility with ElementContent Some strategies (like basic "just overwrite") are compatible with every ElementContent, some others (which e.g. respect particular JSON fields in the content or perform computation on the content) require a certain structure of the content.
    In the end, any deviation (like a modifying party not adhering to the selected strategy, a creating party selecting a strategy which is not compatible with the ElementContent, etc.) will lead to runtime errors and ultimately ignoring an update/create event anyways. So enforcing compatibility by e.g. implementing ElementContent as a trait, concrete content types as structs implementing this trait, and strategies as traits which are only implemented for some of the content stucts may not make much sense considering the additional complexity introduced by such a step.
  • Location of implementation ubisync-lib is supposed to define all necessary standards so arbitrary nodes can still be interoperable, while ubisync provides one possible implementation of a ubisync node. Thus, the definition of Element could be changed where it currently is, and an enum ContentUpdateStrategy containing identifiers of all supported strategies would also be put inside ubisync-lib.
    The concrete implementation of conflict resolution must however be done in ubisync, since it may be tightly coupled with specific properties of the node implementation (like using the selected database efficiently, platform-specific factors, and much more). The Element's ContentUpdateStrategy is deserialized after receiving a message, and based on the value of this field the node starts processing the message as necessary.

To break up complexity, the implementation could be divided into the following parts:

  • Definition of Element and ContentUpdateStrategy (including a default Overwrite variant), rename SetElement message to UpdateElement #12
  • Change ElementCreateRequest to allow apps to choose the appropriate ContentUpdateStrategy (as an option) #13
  • Implement a function for ElementContent which maps a content object to its default suggested strategy #15
  • While handling an UpdateElement message, match on ContentUpdateStrategy to enable future expansion to other conflict resolution strategies #14
  • Implement default Overwrite strategy #16
This is a major feature and very complex. Some thoughts: - **Conflict resolution strategies** ubisync should be as open to different conflict resolution strategies as possible, but since all nodes sharing an element need to be able to interpret it. As a start, every `Element` could have exactly one permanent assigned strategy. Changing the strategy would require creating a new element and either recreating the change history or just using the latest "snapshot" (whatever "latest" may mean in that sense). The strategy would be stored in a member variable of `Element`. It should be possible for an app to handle conflict resolution itself, because there may be very domain-specific ways to handle it. In that case, the `ContentUpdateStrategy` is basically a no-op, passing the message on to the app. - **Default strategies** The global default will most likely be a plain overwrite strategy which replaces the content of an element by any recently received new version of the content. However, for some `ElementContent` variants there may be better strategies which are always applicable. There should be a way to map any `ElementContent` to its default optimal strategy, in case the app does not specify one, to avoid sub-optimal inconsistent overwrite wherever possible. - **Compatibility with `ElementContent`** Some strategies (like basic "just overwrite") are compatible with every `ElementContent`, some others (which e.g. respect particular JSON fields in the content or perform computation on the content) require a certain structure of the content. In the end, any deviation (like a modifying party not adhering to the selected strategy, a creating party selecting a strategy which is not compatible with the `ElementContent`, etc.) will lead to runtime errors and ultimately ignoring an update/create event anyways. So enforcing compatibility by e.g. implementing `ElementContent` as a trait, concrete content types as structs implementing this trait, and strategies as traits which are only implemented for some of the content stucts *may* not make much sense considering the additional complexity introduced by such a step. - **Location of implementation** `ubisync-lib` is supposed to define all necessary standards so arbitrary nodes can still be interoperable, while `ubisync` provides one possible implementation of a ubisync node. Thus, the definition of `Element` could be changed where it currently is, and an enum `ContentUpdateStrategy` containing identifiers of all supported strategies would also be put inside `ubisync-lib`. The concrete implementation of conflict resolution must however be done in `ubisync`, since it may be tightly coupled with specific properties of the node implementation (like using the selected database efficiently, platform-specific factors, and much more). The `Element`'s `ContentUpdateStrategy` is deserialized after receiving a message, and based on the value of this field the node starts processing the message as necessary. To break up complexity, the implementation could be divided into the following parts: - Definition of `Element` and `ContentUpdateStrategy` (including a default Overwrite variant), rename `SetElement` message to `UpdateElement` #12 - Change `ElementCreateRequest` to allow apps to choose the appropriate `ContentUpdateStrategy` (as an option) #13 - Implement a function for `ElementContent` which maps a content object to its default suggested strategy #15 - While handling an `UpdateElement` message, match on `ContentUpdateStrategy` to enable future expansion to other conflict resolution strategies #14 - Implement default `Overwrite` strategy #16
philip added this to the v1 milestone 2024-02-13 16:20:48 +00:00
philip added the
crate/ubisync-lib
crate/ubisync
Kind/Feature
labels 2024-02-13 16:20:48 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: philip/ubisync#17
No description provided.