mergeful-0.3.0.0
Safe HaskellNone
LanguageHaskell2010

Data.Mergeful.Item

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 central server should operate as follows:

The server starts with an initialServerItem.

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

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.

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

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

Instances details
Generic (ItemMergeStrategy a) Source # 
Instance details

Defined in Data.Mergeful.Item

Associated Types

type Rep (ItemMergeStrategy a) :: Type -> Type #

type Rep (ItemMergeStrategy a) Source # 
Instance details

Defined in Data.Mergeful.Item

type Rep (ItemMergeStrategy a) = D1 ('MetaData "ItemMergeStrategy" "Data.Mergeful.Item" "mergeful-0.3.0.0-IufP8wiUmUb8vtwEhWnzS0" 'False) (C1 ('MetaCons "ItemMergeStrategy" 'PrefixI 'True) (S1 ('MetaSel ('Just "itemMergeStrategyMergeChangeConflict") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (a -> a -> ChangeConflictResolution a)) :*: (S1 ('MetaSel ('Just "itemMergeStrategyMergeClientDeletedConflict") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (a -> ClientDeletedConflictResolution)) :*: S1 ('MetaSel ('Just "itemMergeStrategyMergeServerDeletedConflict") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (a -> ServerDeletedConflictResolution)))))

data ChangeConflictResolution a Source #

Constructors

KeepLocal 
TakeRemote 
Merged a 

Instances

Instances details
Eq a => Eq (ChangeConflictResolution a) Source # 
Instance details

Defined in Data.Mergeful.Item

Show a => Show (ChangeConflictResolution a) Source # 
Instance details

Defined in Data.Mergeful.Item

Generic (ChangeConflictResolution a) Source # 
Instance details

Defined in Data.Mergeful.Item

Associated Types

type Rep (ChangeConflictResolution a) :: Type -> Type #

type Rep (ChangeConflictResolution a) Source # 
Instance details

Defined in Data.Mergeful.Item

type Rep (ChangeConflictResolution a) = D1 ('MetaData "ChangeConflictResolution" "Data.Mergeful.Item" "mergeful-0.3.0.0-IufP8wiUmUb8vtwEhWnzS0" 'False) (C1 ('MetaCons "KeepLocal" 'PrefixI 'False) (U1 :: Type -> Type) :+: (C1 ('MetaCons "TakeRemote" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "Merged" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 a))))

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

Instances details
Eq a => Eq (ItemMergeResult a) Source # 
Instance details

Defined in Data.Mergeful.Item

Show a => Show (ItemMergeResult a) Source # 
Instance details

Defined in Data.Mergeful.Item

Generic (ItemMergeResult a) Source # 
Instance details

Defined in Data.Mergeful.Item

Associated Types

type Rep (ItemMergeResult a) :: Type -> Type #

NFData a => NFData (ItemMergeResult a) Source # 
Instance details

Defined in Data.Mergeful.Item

Methods

rnf :: ItemMergeResult a -> () #

Validity a => Validity (ItemMergeResult a) Source # 
Instance details

Defined in Data.Mergeful.Item

type Rep (ItemMergeResult a) Source # 
Instance details

Defined in Data.Mergeful.Item

type Rep (ItemMergeResult a) = D1 ('MetaData "ItemMergeResult" "Data.Mergeful.Item" "mergeful-0.3.0.0-IufP8wiUmUb8vtwEhWnzS0" 'False) ((C1 ('MetaCons "MergeSuccess" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (ClientItem a))) :+: C1 ('MetaCons "MergeConflict" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 a) :*: S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Timed a)))) :+: (C1 ('MetaCons "MergeConflictClientDeleted" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Timed a))) :+: (C1 ('MetaCons "MergeConflictServerDeleted" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 a)) :+: C1 ('MetaCons "MergeMismatch" 'PrefixI 'False) (U1 :: Type -> Type))))

Server side

initialServerItem :: ServerItem a Source #

A server item to start with.

It contains no value.

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

Instances details
Eq a => Eq (ClientItem a) Source # 
Instance details

Defined in Data.Mergeful.Item

Methods

(==) :: ClientItem a -> ClientItem a -> Bool #

(/=) :: ClientItem a -> ClientItem a -> Bool #

Show a => Show (ClientItem a) Source # 
Instance details

Defined in Data.Mergeful.Item

Generic (ClientItem a) Source # 
Instance details

Defined in Data.Mergeful.Item

Associated Types

type Rep (ClientItem a) :: Type -> Type #

Methods

from :: ClientItem a -> Rep (ClientItem a) x #

to :: Rep (ClientItem a) x -> ClientItem a #

HasCodec a => ToJSON (ClientItem a) Source # 
Instance details

Defined in Data.Mergeful.Item

HasCodec a => FromJSON (ClientItem a) Source # 
Instance details

Defined in Data.Mergeful.Item

HasCodec a => HasCodec (ClientItem a) Source # 
Instance details

Defined in Data.Mergeful.Item

NFData a => NFData (ClientItem a) Source # 
Instance details

Defined in Data.Mergeful.Item

Methods

rnf :: ClientItem a -> () #

Validity a => Validity (ClientItem a) Source # 
Instance details

Defined in Data.Mergeful.Item

type Rep (ClientItem a) Source # 
Instance details

Defined in Data.Mergeful.Item

type Rep (ClientItem a) = D1 ('MetaData "ClientItem" "Data.Mergeful.Item" "mergeful-0.3.0.0-IufP8wiUmUb8vtwEhWnzS0" 'False) ((C1 ('MetaCons "ClientEmpty" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "ClientAdded" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 a))) :+: (C1 ('MetaCons "ClientItemSynced" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Timed a))) :+: (C1 ('MetaCons "ClientItemSyncedButChanged" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Timed a))) :+: C1 ('MetaCons "ClientDeleted" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 ServerTime)))))

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 ServerTime

ItemSyncRequestKnownButChanged !(Timed a)

There is an item locally that was synced at the given ServerTime but it has been changed since then.

ItemSyncRequestDeletedLocally !ServerTime

There was an item locally that has been deleted but the deletion wasn't synced to the server yet.

Instances

Instances details
Eq a => Eq (ItemSyncRequest a) Source # 
Instance details

Defined in Data.Mergeful.Item

Show a => Show (ItemSyncRequest a) Source # 
Instance details

Defined in Data.Mergeful.Item

Generic (ItemSyncRequest a) Source # 
Instance details

Defined in Data.Mergeful.Item

Associated Types

type Rep (ItemSyncRequest a) :: Type -> Type #

HasCodec a => ToJSON (ItemSyncRequest a) Source # 
Instance details

Defined in Data.Mergeful.Item

HasCodec a => FromJSON (ItemSyncRequest a) Source # 
Instance details

Defined in Data.Mergeful.Item

HasCodec a => HasCodec (ItemSyncRequest a) Source # 
Instance details

Defined in Data.Mergeful.Item

NFData a => NFData (ItemSyncRequest a) Source # 
Instance details

Defined in Data.Mergeful.Item

Methods

rnf :: ItemSyncRequest a -> () #

Validity a => Validity (ItemSyncRequest a) Source # 
Instance details

Defined in Data.Mergeful.Item

type Rep (ItemSyncRequest a) Source # 
Instance details

Defined in Data.Mergeful.Item

type Rep (ItemSyncRequest a) = D1 ('MetaData "ItemSyncRequest" "Data.Mergeful.Item" "mergeful-0.3.0.0-IufP8wiUmUb8vtwEhWnzS0" 'False) ((C1 ('MetaCons "ItemSyncRequestPoll" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "ItemSyncRequestNew" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 a))) :+: (C1 ('MetaCons "ItemSyncRequestKnown" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 ServerTime)) :+: (C1 ('MetaCons "ItemSyncRequestKnownButChanged" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Timed a))) :+: C1 ('MetaCons "ItemSyncRequestDeletedLocally" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 ServerTime)))))

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

Instances details
Eq a => Eq (ItemSyncResponse a) Source # 
Instance details

Defined in Data.Mergeful.Item

Show a => Show (ItemSyncResponse a) Source # 
Instance details

Defined in Data.Mergeful.Item

Generic (ItemSyncResponse a) Source # 
Instance details

Defined in Data.Mergeful.Item

Associated Types

type Rep (ItemSyncResponse a) :: Type -> Type #

HasCodec a => ToJSON (ItemSyncResponse a) Source # 
Instance details

Defined in Data.Mergeful.Item

HasCodec a => FromJSON (ItemSyncResponse a) Source # 
Instance details

Defined in Data.Mergeful.Item

HasCodec a => HasCodec (ItemSyncResponse a) Source # 
Instance details

Defined in Data.Mergeful.Item

NFData a => NFData (ItemSyncResponse a) Source # 
Instance details

Defined in Data.Mergeful.Item

Methods

rnf :: ItemSyncResponse a -> () #

Validity a => Validity (ItemSyncResponse a) Source # 
Instance details

Defined in Data.Mergeful.Item

type Rep (ItemSyncResponse a) Source # 
Instance details

Defined in Data.Mergeful.Item

type Rep (ItemSyncResponse a) = D1 ('MetaData "ItemSyncResponse" "Data.Mergeful.Item" "mergeful-0.3.0.0-IufP8wiUmUb8vtwEhWnzS0" 'False) (((C1 ('MetaCons "ItemSyncResponseInSyncEmpty" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "ItemSyncResponseInSyncFull" 'PrefixI 'False) (U1 :: Type -> Type)) :+: (C1 ('MetaCons "ItemSyncResponseClientAdded" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 ServerTime)) :+: (C1 ('MetaCons "ItemSyncResponseClientChanged" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 ServerTime)) :+: C1 ('MetaCons "ItemSyncResponseClientDeleted" 'PrefixI 'False) (U1 :: Type -> Type)))) :+: ((C1 ('MetaCons "ItemSyncResponseServerAdded" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Timed a))) :+: (C1 ('MetaCons "ItemSyncResponseServerChanged" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Timed a))) :+: C1 ('MetaCons "ItemSyncResponseServerDeleted" 'PrefixI 'False) (U1 :: Type -> Type))) :+: (C1 ('MetaCons "ItemSyncResponseConflict" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Timed a))) :+: (C1 ('MetaCons "ItemSyncResponseConflictClientDeleted" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Timed a))) :+: C1 ('MetaCons "ItemSyncResponseConflictServerDeleted" 'PrefixI 'False) (U1 :: Type -> Type)))))

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 ServerTime.

Instances

Instances details
Eq a => Eq (ServerItem a) Source # 
Instance details

Defined in Data.Mergeful.Item

Methods

(==) :: ServerItem a -> ServerItem a -> Bool #

(/=) :: ServerItem a -> ServerItem a -> Bool #

Show a => Show (ServerItem a) Source # 
Instance details

Defined in Data.Mergeful.Item

Generic (ServerItem a) Source # 
Instance details

Defined in Data.Mergeful.Item

Associated Types

type Rep (ServerItem a) :: Type -> Type #

Methods

from :: ServerItem a -> Rep (ServerItem a) x #

to :: Rep (ServerItem a) x -> ServerItem a #

HasCodec a => ToJSON (ServerItem a) Source # 
Instance details

Defined in Data.Mergeful.Item

HasCodec a => FromJSON (ServerItem a) Source # 
Instance details

Defined in Data.Mergeful.Item

HasCodec a => HasCodec (ServerItem a) Source # 
Instance details

Defined in Data.Mergeful.Item

NFData a => NFData (ServerItem a) Source # 
Instance details

Defined in Data.Mergeful.Item

Methods

rnf :: ServerItem a -> () #

Validity a => Validity (ServerItem a) Source # 
Instance details

Defined in Data.Mergeful.Item

type Rep (ServerItem a) Source # 
Instance details

Defined in Data.Mergeful.Item

type Rep (ServerItem a) = D1 ('MetaData "ServerItem" "Data.Mergeful.Item" "mergeful-0.3.0.0-IufP8wiUmUb8vtwEhWnzS0" 'False) (C1 ('MetaCons "ServerEmpty" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "ServerFull" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Timed a))))