Safe Haskell | None |
---|---|
Language | Haskell2010 |
Data.Mergeful.Value
Contents
Description
A way to synchronise a single value with safe merge conflicts.
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:
For the first sychronisation
The client should ask the server for the current server value.
The server should send over a Timed
vaule, and the client should create its ClientValue
with initialClientValue
.
For any following synchronisation:
- The client produces a
ValueSyncRequest
withmakeValueSyncRequest
. - The client sends that request to the central server and gets a
ValueSyncResponse
. - The client then updates its local store with
mergeValueSyncResponseRaw
ormergeValueSyncResponseIgnoreProblems
.
The central server should operate as follows:
- The server should create an initial
ServerValue
usinginitialServerValue
. - The server accepts a
ValueSyncRequest
. - The server performs operations according to the functionality of
processServerValueSync
. - The server respons with a
ValueSyncResponse
.
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
- initialClientValue :: Timed a -> ClientValue a
- makeValueSyncRequest :: ClientValue a -> ValueSyncRequest a
- mergeValueSyncResponseRaw :: ClientValue a -> ValueSyncResponse a -> ValueMergeResult a
- data ValueMergeResult a
- = MergeSuccess !(ClientValue a)
- | MergeConflict !a !(Timed a)
- | MergeMismatch
- mergeValueSyncResponseIgnoreProblems :: ClientValue a -> ValueSyncResponse a -> ClientValue a
- mergeIgnoringProblems :: ClientValue a -> ValueMergeResult a -> ClientValue a
- mergeFromServer :: ClientValue a -> ValueMergeResult a -> ClientValue a
- mergeUsingFunction :: (a -> Timed a -> Timed a) -> ClientValue a -> ValueMergeResult a -> ClientValue a
- initialServerValue :: a -> ServerValue a
- processServerValueSync :: ServerValue a -> ValueSyncRequest a -> (ValueSyncResponse a, ServerValue a)
- data ChangedFlag
- data ClientValue a = ClientValue {
- clientValueTimedValue :: !(Timed a)
- clientValueChanged :: !ChangedFlag
- data ValueSyncRequest a
- data ValueSyncResponse a
- newtype ServerValue a = ServerValue {
- unServerValue :: Timed a
Documentation
initialClientValue :: Timed a -> ClientValue a Source #
Produce a client value based on an initial synchronisation request
makeValueSyncRequest :: ClientValue a -> ValueSyncRequest a Source #
Produce an ItemSyncRequest
from a ClientItem
.
Send this to the server for synchronisation.
mergeValueSyncResponseRaw :: ClientValue a -> ValueSyncResponse a -> ValueMergeResult a Source #
Merge an ValueSyncResponse
into the current ClientValue
.
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 ValueMergeResult
so you can decide what to do with it.
data ValueMergeResult a Source #
Constructors
MergeSuccess !(ClientValue a) | The merger went succesfully, no conflicts or desyncs |
MergeConflict !a !(Timed a) | The item at the server side |
MergeMismatch | The server responded with a response that did not make sense given the client's request. This should not happen in practice. |
Instances
mergeValueSyncResponseIgnoreProblems :: ClientValue a -> ValueSyncResponse a -> ClientValue a Source #
Resolve a ValueSyncResponse
into the current ClientValue
.
This function ignores any problems that may occur. In the case of a conclict, it will just not update the client item. The next sync request will then produce a conflict again.
mergeValueSyncResponseIgnoreProblems cs = mergeIgnoringProblems cs . mergeValueSyncResponseRaw cs
mergeIgnoringProblems :: ClientValue a -> ValueMergeResult a -> ClientValue a Source #
Ignore any merge problems in a ValueMergeResult
.
This function just returns the original ClientValue
if anything other than MergeSuccess
occurs.
This function ignores any problems that may occur. In the case of a conclict, it will just not update the client item. The next sync request will then produce a conflict again.
Pro: does not lose data
Con: Clients will diverge when a conflict occurs
mergeFromServer :: ClientValue a -> ValueMergeResult a -> ClientValue a Source #
Resolve a ValueMergeResult
by taking whatever the server gave the client.
Pro: Clients will converge on the same value.
Con: Conflicting updates will be lost.
mergeUsingFunction :: (a -> Timed a -> Timed a) -> ClientValue a -> ValueMergeResult a -> ClientValue a Source #
Resolve a ValueMergeResult
using a given merge strategy.
This function ignores MergeMismatch
and will just return the original ClientValue
in that case.
In order for clients to converge on the same value correctly, this function must be:
- Associative
- Idempotent
- The same on all clients
Server side
initialServerValue :: a -> ServerValue a Source #
Initialise a server value.
Note that the server has to start with a value, the value a
cannot be omitted.
processServerValueSync :: ServerValue a -> ValueSyncRequest a -> (ValueSyncResponse a, ServerValue a) Source #
Serve an ValueSyncRequest
using the current ServerValue
, producing an ValueSyncResponse
and a new ServerValue
.
Types, for reference
data ChangedFlag Source #
Constructors
Changed | |
NotChanged |
Instances
Eq ChangedFlag Source # | |
Defined in Data.Mergeful.Value | |
Show ChangedFlag Source # | |
Defined in Data.Mergeful.Value Methods showsPrec :: Int -> ChangedFlag -> ShowS # show :: ChangedFlag -> String # showList :: [ChangedFlag] -> ShowS # | |
Generic ChangedFlag Source # | |
Defined in Data.Mergeful.Value Associated Types type Rep ChangedFlag :: Type -> Type # | |
ToJSON ChangedFlag Source # | |
Defined in Data.Mergeful.Value Methods toJSON :: ChangedFlag -> Value # toEncoding :: ChangedFlag -> Encoding # toJSONList :: [ChangedFlag] -> Value # toEncodingList :: [ChangedFlag] -> Encoding # | |
FromJSON ChangedFlag Source # | |
Defined in Data.Mergeful.Value | |
HasCodec ChangedFlag Source # | |
Defined in Data.Mergeful.Value | |
NFData ChangedFlag Source # | |
Defined in Data.Mergeful.Value Methods rnf :: ChangedFlag -> () # | |
Validity ChangedFlag Source # | |
Defined in Data.Mergeful.Value Methods validate :: ChangedFlag -> Validation # | |
type Rep ChangedFlag Source # | |
data ClientValue a Source #
The client side value.
The only differences between a
and 'ClientValue a' are that
'ClientValue a' also remembers the last synchronisation time from
the server, and whether the item has been modified at the client
There cannot be an unsynced ClientValue
.
Constructors
ClientValue | |
Fields
|
Instances
data ValueSyncRequest a Source #
Constructors
ValueSyncRequestKnown !ServerTime | There is an item locally that was synced at the given |
ValueSyncRequestKnownButChanged !(Timed a) | There is an item locally that was synced at the given |
Instances
data ValueSyncResponse a Source #
Constructors
ValueSyncResponseInSync | The client and server are fully in sync. Nothing needs to be done at the client side. |
ValueSyncResponseClientChanged !ServerTime | The client changed the value and server has succesfully been made aware of that. The client needs to update its server time |
ValueSyncResponseServerChanged !(Timed a) | This value has been changed on the server side. The client should change it too. |
ValueSyncResponseConflict !(Timed a) | The item at the server side |
Instances
newtype ServerValue a Source #
The server-side value.
The only difference between a
and 'ServerValue a' is that 'ServerValue a' also
remembers the last time this value was changed during synchronisation.
Constructors
ServerValue | |
Fields
|