Safe Haskell | None |
---|---|
Language | Haskell2010 |
Data.Mergeful.Item
Contents
Description
A way to synchronise an item with safe merge conflicts.
The item is "zero or one" value.
One could say that Item a = Maybe a
but there are so such types here.
This methaphor just serves as explanation
The setup is as follows:
- A central server is set up to synchronise with
- Each client synchronises with the central server, but never with eachother
A client should operate as follows:
The clients starts with an initialClientItem
.
- The client produces a
ItemSyncRequest
withmakeItemSyncRequest
. - The client sends that request to the central server and gets a
ItemSyncResponse
. - The client then updates its local store with
mergeItemSyncResponseRaw
ormergeItemSyncResponseIgnoreProblems
.
The central server should operate as follows:
The server starts with an initialServerItem
.
- The server accepts a
ItemSyncRequest
. - The server performs operations according to the functionality of
processServerItemSync
. - The server respons with a
ItemSyncResponse
.
WARNING: This whole approach can break down if a server resets its server times or if a client syncs with two different servers using the same server times.
Synopsis
- initialClientItem :: ClientItem a
- initialItemSyncRequest :: ItemSyncRequest a
- makeItemSyncRequest :: ClientItem a -> ItemSyncRequest a
- mergeItemSyncResponseFromServer :: ClientItem a -> ItemSyncResponse a -> ClientItem a
- mergeItemSyncResponseFromClient :: ClientItem a -> ItemSyncResponse a -> ClientItem a
- mergeItemSyncResponseUsingCRDT :: (a -> a -> a) -> ClientItem a -> ItemSyncResponse a -> ClientItem a
- mergeItemSyncResponseUsingStrategy :: ItemMergeStrategy a -> ClientItem a -> ItemSyncResponse a -> ClientItem a
- mergeFromServer :: ClientItem a -> ItemMergeResult a -> ClientItem a
- mergeFromServerStrategy :: ItemMergeStrategy a
- mergeFromClient :: ClientItem a -> ItemMergeResult a -> ClientItem a
- mergeFromClientStrategy :: ItemMergeStrategy a
- mergeUsingCRDT :: (a -> a -> a) -> ClientItem a -> ItemMergeResult a -> ClientItem a
- mergeUsingCRDTStrategy :: (a -> a -> a) -> ItemMergeStrategy a
- data ItemMergeStrategy a = ItemMergeStrategy {}
- data ChangeConflictResolution a
- = KeepLocal
- | TakeRemote
- | Merged a
- data ClientDeletedConflictResolution
- data ServerDeletedConflictResolution
- mergeUsingStrategy :: ItemMergeStrategy a -> ClientItem a -> ItemMergeResult a -> ClientItem a
- mergeItemSyncResponseRaw :: ClientItem a -> ItemSyncResponse a -> ItemMergeResult a
- data ItemMergeResult a
- = MergeSuccess !(ClientItem a)
- | MergeConflict !a !(Timed a)
- | MergeConflictClientDeleted !(Timed a)
- | MergeConflictServerDeleted !a
- | MergeMismatch
- initialServerItem :: ServerItem a
- processServerItemSync :: ServerItem a -> ItemSyncRequest a -> (ItemSyncResponse a, ServerItem a)
- data ClientItem a
- = ClientEmpty
- | ClientAdded !a
- | ClientItemSynced !(Timed a)
- | ClientItemSyncedButChanged !(Timed a)
- | ClientDeleted !ServerTime
- data ItemSyncRequest a
- data ItemSyncResponse a
- = ItemSyncResponseInSyncEmpty
- | ItemSyncResponseInSyncFull
- | ItemSyncResponseClientAdded !ServerTime
- | ItemSyncResponseClientChanged !ServerTime
- | ItemSyncResponseClientDeleted
- | ItemSyncResponseServerAdded !(Timed a)
- | ItemSyncResponseServerChanged !(Timed a)
- | ItemSyncResponseServerDeleted
- | ItemSyncResponseConflict !(Timed a)
- | ItemSyncResponseConflictClientDeleted !(Timed a)
- | ItemSyncResponseConflictServerDeleted
- data ServerItem a
- = ServerEmpty
- | ServerFull !(Timed a)
Documentation
initialClientItem :: ClientItem a Source #
A client item to start with.
It contains no value.
initialItemSyncRequest :: ItemSyncRequest a Source #
An intial ItemSyncRequest
to start with.
It just asks the server to send over whatever it knows.
makeItemSyncRequest :: ClientItem a -> ItemSyncRequest a Source #
Produce an ItemSyncRequest
from a ClientItem
.
Send this to the server for synchronisation.
mergeItemSyncResponseFromServer :: ClientItem a -> ItemSyncResponse a -> ClientItem a Source #
mergeItemSyncResponseFromClient :: ClientItem a -> ItemSyncResponse a -> ClientItem a Source #
mergeItemSyncResponseUsingCRDT :: (a -> a -> a) -> ClientItem a -> ItemSyncResponse a -> ClientItem a Source #
mergeItemSyncResponseUsingStrategy :: ItemMergeStrategy a -> ClientItem a -> ItemSyncResponse a -> ClientItem a Source #
mergeFromServer :: ClientItem a -> ItemMergeResult a -> ClientItem a Source #
Resolve an ItemMergeResult
by taking whatever the server gave the client.
Pro: Clients will converge on the same value.
Con: Conflicting updates will be lost.
mergeFromServerStrategy :: ItemMergeStrategy a Source #
A merge strategy that takes whatever the server gave the client.
Pro: Clients will converge on the same value.
Con: Conflicting updates will be lost.
mergeFromClient :: ClientItem a -> ItemMergeResult a -> ClientItem a Source #
Resolve an ItemMergeResult
by keeping whatever the client had.
Pro: does not lose data
Con: Clients will diverge when a conflict occurs
mergeFromClientStrategy :: ItemMergeStrategy a Source #
A merge strategy that keeps whatever the client had.
Pro: does not lose data
Con: Clients will diverge when a conflict occurs
mergeUsingCRDT :: (a -> a -> a) -> ClientItem a -> ItemMergeResult a -> ClientItem a Source #
mergeUsingCRDTStrategy :: (a -> a -> a) -> ItemMergeStrategy a Source #
A merge strategy that uses a CRDT merging function to merge items.
In case of other-than-change conflicts, this will be the same as the mergeFromServerStrategy
strategy.
If this is not what you want, create your own ItemMergeStrategy
manually.
data ItemMergeStrategy a Source #
A strategy to merge conflicts for item synchronisation
Constructors
ItemMergeStrategy | |
Fields
|
Instances
data ChangeConflictResolution a Source #
Constructors
KeepLocal | |
TakeRemote | |
Merged a |
Instances
data ClientDeletedConflictResolution Source #
Constructors
TakeRemoteChange | |
StayDeleted |
Instances
Eq ClientDeletedConflictResolution Source # | |
Defined in Data.Mergeful.Item | |
Show ClientDeletedConflictResolution Source # | |
Defined in Data.Mergeful.Item Methods showsPrec :: Int -> ClientDeletedConflictResolution -> ShowS # | |
Generic ClientDeletedConflictResolution Source # | |
Defined in Data.Mergeful.Item Associated Types type Rep ClientDeletedConflictResolution :: Type -> Type # | |
type Rep ClientDeletedConflictResolution Source # | |
Defined in Data.Mergeful.Item type Rep ClientDeletedConflictResolution = D1 ('MetaData "ClientDeletedConflictResolution" "Data.Mergeful.Item" "mergeful-0.3.0.0-IufP8wiUmUb8vtwEhWnzS0" 'False) (C1 ('MetaCons "TakeRemoteChange" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "StayDeleted" 'PrefixI 'False) (U1 :: Type -> Type)) |
data ServerDeletedConflictResolution Source #
Constructors
KeepLocalChange | |
Delete |
Instances
Eq ServerDeletedConflictResolution Source # | |
Defined in Data.Mergeful.Item | |
Show ServerDeletedConflictResolution Source # | |
Defined in Data.Mergeful.Item Methods showsPrec :: Int -> ServerDeletedConflictResolution -> ShowS # | |
Generic ServerDeletedConflictResolution Source # | |
Defined in Data.Mergeful.Item Associated Types type Rep ServerDeletedConflictResolution :: Type -> Type # | |
type Rep ServerDeletedConflictResolution Source # | |
Defined in Data.Mergeful.Item |
mergeUsingStrategy :: ItemMergeStrategy a -> ClientItem a -> ItemMergeResult a -> ClientItem a Source #
Resolve an ItemMergeResult
using a given merge strategy.
This function ignores MergeMismatch
and will just return the original ClientItem
in that case.
In order for clients to converge on the same item correctly, this function must be:
- Associative
- Idempotent
- The same on all clients
mergeItemSyncResponseRaw :: ClientItem a -> ItemSyncResponse a -> ItemMergeResult a Source #
Merge an ItemSyncResponse
into the current ClientItem
.
This function will not make any decisions about what to do with
conflicts or mismatches between the request and the response.
It only produces a ItemMergeResult
so you can decide what to do with it.
data ItemMergeResult a Source #
Constructors
MergeSuccess !(ClientItem a) | The merger went succesfully, no conflicts or desyncs |
MergeConflict !a !(Timed a) | The item at the server side | There was a merge conflict. The client had deleted the item while the server had modified it. |
MergeConflictClientDeleted !(Timed a) | The item at the server side | There was a merge conflict. The server had deleted the item while the client had modified it. |
MergeConflictServerDeleted !a | The item at the client side | The server responded with a response that did not make sense given the client's request. |
MergeMismatch |
Instances
Server side
initialServerItem :: ServerItem a Source #
A server item to start with.
It contains no value.
processServerItemSync :: ServerItem a -> ItemSyncRequest a -> (ItemSyncResponse a, ServerItem a) Source #
Serve an ItemSyncRequest
using the current ServerItem
, producing an ItemSyncResponse
and a new ServerItem
.
Types, for reference
data ClientItem a Source #
Constructors
ClientEmpty | There is no item on the client side |
ClientAdded !a | There is is an item but the server is not aware of it yet. |
ClientItemSynced !(Timed a) | There is is an item and it has been synced with the server. |
ClientItemSyncedButChanged !(Timed a) | There is is an item and it has been synced with the server, but it has since been modified. |
ClientDeleted !ServerTime | There was an item, and it has been deleted locally, but the server has not been made aware of this. |
Instances
data ItemSyncRequest a Source #
Constructors
ItemSyncRequestPoll | There is no item locally |
ItemSyncRequestNew !a | There is an item locally that has not been synced to the server yet. |
ItemSyncRequestKnown !ServerTime | There is an item locally that was synced at the given |
ItemSyncRequestKnownButChanged !(Timed a) | There is an item locally that was synced at the given |
ItemSyncRequestDeletedLocally !ServerTime | There was an item locally that has been deleted but the deletion wasn't synced to the server yet. |
Instances
data ItemSyncResponse a Source #
Constructors
ItemSyncResponseInSyncEmpty | The client and server are fully in sync, and both empty Nothing needs to be done at the client side. |
ItemSyncResponseInSyncFull | The client and server are fully in sync. Nothing needs to be done at the client side. |
ItemSyncResponseClientAdded !ServerTime | The client added an item and server has succesfully been made aware of that. The client needs to update its server time |
ItemSyncResponseClientChanged !ServerTime | The client changed an item and server has succesfully been made aware of that. The client needs to update its server time |
ItemSyncResponseClientDeleted | The client deleted an item and server has succesfully been made aware of that. The client can delete it from its deleted items |
ItemSyncResponseServerAdded !(Timed a) | This item has been added on the server side The client should add it too. |
ItemSyncResponseServerChanged !(Timed a) | This item has been modified on the server side. The client should modify it too. |
ItemSyncResponseServerDeleted | The item was deleted on the server side The client should delete it too. |
ItemSyncResponseConflict !(Timed a) | The item at the server side | A conflict occurred. The server has an item but the client does not. The server kept its part, the client can either take whatever the server gave them or deal with the conflict somehow, and then try to re-sync. |
ItemSyncResponseConflictClientDeleted !(Timed a) | The item at the server side | A conflict occurred. The client has a (modified) item but the server does not have any item. The server left its item deleted, the client can either delete its item too or deal with the conflict somehow, and then try to re-sync. |
ItemSyncResponseConflictServerDeleted |
Instances
data ServerItem a Source #
Constructors
ServerEmpty | There is no item on the server side |
ServerFull !(Timed a) | There is an item on the server side, and it was last synced at the given |