legion 0.6.0.0 → 0.7.0.0
raw patch · 14 files changed
+322/−322 lines, 14 files
Files
- legion.cabal +1/−1
- src/Network/Legion.hs +9/−9
- src/Network/Legion/Admin.hs +8/−8
- src/Network/Legion/Application.hs +10/−10
- src/Network/Legion/Basics.hs +15/−15
- src/Network/Legion/ClusterState.hs +6/−6
- src/Network/Legion/Distribution.hs +1/−3
- src/Network/Legion/PartitionState.hs +34/−34
- src/Network/Legion/PowerState.hs +100/−98
- src/Network/Legion/Propagation.hs +16/−16
- src/Network/Legion/Runtime.hs +53/−53
- src/Network/Legion/Runtime/ConnectionManager.hs +23/−23
- src/Network/Legion/Runtime/PeerMessage.hs +7/−7
- src/Network/Legion/StateMachine.hs +39/−39
legion.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: legion-version: 0.6.0.0+version: 0.7.0.0 synopsis: Distributed, stateful, homogeneous microservice framework. description: Legion is a framework for writing distributed, homogeneous, stateful microservices in Haskell.
src/Network/Legion.hs view
@@ -47,7 +47,7 @@ Indexable(..), LegionConstraints, Persistence(..),- ApplyDelta(..),+ Event(..), Tag(..), -- * Other Types@@ -71,7 +71,7 @@ Indexable(indexEntries)) import Network.Legion.PartitionKey (PartitionKey(K, unKey)) import Network.Legion.PartitionState (PartitionPowerState)-import Network.Legion.PowerState (ApplyDelta(apply))+import Network.Legion.PowerState (Event(apply)) import Network.Legion.Runtime (StartupMode(NewCluster, JoinCluster), forkLegionary, Runtime, makeRequest, search) import Network.Legion.Settings (RuntimeSettings(RuntimeSettings,@@ -115,14 +115,14 @@ -- The only thing required to implement a legion service is to implement -- a few typeclasses and call 'forkLegionary'. The state-aware part of -- your application will live mostly within the request handler, which--- is implemented via a typeclass `ApplyDelta`.+-- is implemented via a typeclass `Event`. -- --- > class ApplyDelta i o s | i s -> o where--- > apply :: i -> s -> (o, s)+-- > class Event e o s | e -> s o where+-- > apply :: e -> s -> (o, s) -- -- If you look at 'apply', you will see that it is abstract over the type--- variables @i@, @o@, and @s@. These are the types your application--- has to fill in. @i@ stands for "input", which is the type of requests+-- variables @e@, @o@, and @s@. These are the types your application+-- has to fill in. @e@ stands for "event", which is the type of requests -- your application accepts; @o@ stands for "output", which is the type of -- responses your application will generate in response to those requests, -- and @s@ stands for "state", which is the application state that each@@ -131,8 +131,8 @@ -- Implementing a request handler is pretty straight forward, but -- there is a little bit more to it than meets the eye. If you look at -- 'forkLegionary', you will see a constraint named @'LegionConstraints'--- i o s@, which is short-hand for a long list of typeclasses that your--- @i@, @o@, and @s@ types are going to have to implement.+-- e o s@, which is short-hand for a long list of typeclasses that your+-- @e@, @o@, and @s@ types are going to have to implement. -- -- The persistence layer provides the framework with a way to store the -- various partition states. This allows you to choose any number of
src/Network/Legion/Admin.hs view
@@ -44,10 +44,10 @@ {- | Start the admin service in a background thread. -}-runAdmin :: (LegionConstraints i o s)+runAdmin :: (LegionConstraints e o s) => Port -> HostPreference- -> LIO (Source LIO (AdminMessage i o s))+ -> LIO (Source LIO (AdminMessage e o s)) runAdmin addr host = do logging <- askLoggerIO chan <- lift newChan@@ -81,8 +81,8 @@ return (chanToSource chan) where send- :: Chan (AdminMessage i o s)- -> ((a -> LIO ()) -> AdminMessage i o s)+ :: Chan (AdminMessage e o s)+ -> ((a -> LIO ()) -> AdminMessage e o s) -> ActionT Text LIO a send chan msg = lift . lift $ do mvar <- newEmptyMVar@@ -127,12 +127,12 @@ {- | The type of messages sent by the admin service. -}-data AdminMessage i o s- = GetState (NodeState i o s -> LIO ())- | GetPart PartitionKey (Maybe (PartitionPowerState i o s) -> LIO ())+data AdminMessage e o s+ = GetState (NodeState e o s -> LIO ())+ | GetPart PartitionKey (Maybe (PartitionPowerState e o s) -> LIO ()) | Eject Peer (() -> LIO ()) -instance Show (AdminMessage i o s) where+instance Show (AdminMessage e o s) where show (GetState _) = "(GetState _)" show (GetPart k _) = "(GetPart " ++ show k ++ " _)" show (Eject p _) = "(Eject " ++ show p ++ " _)"
src/Network/Legion/Application.hs view
@@ -14,20 +14,20 @@ import Network.Legion.Index (Indexable) import Network.Legion.PartitionKey (PartitionKey) import Network.Legion.PartitionState (PartitionPowerState)-import Network.Legion.PowerState (ApplyDelta)+import Network.Legion.PowerState (Event) {- | This is a more convenient way to write the somewhat unwieldy set of constraints > (- > ApplyDelta i o s, Default s, Binary i, Binary o, Binary s, Show i,- > Show o, Show s, Eq i+ > Event e o s, Default s, Binary e, Binary o, Binary s, Show e,+ > Show o, Show s, Eq e > ) -}-type LegionConstraints i o s = (- ApplyDelta i o s, Indexable s, Default s, Binary i, Binary o, Binary s,- Show i, Show o, Show s, Eq i+type LegionConstraints e o s = (+ Event e o s, Indexable s, Default s, Binary e, Binary o, Binary s,+ Show e, Show o, Show s, Eq e ) @@ -36,10 +36,10 @@ partition states. See 'Network.Legion.newMemoryPersistence' or 'Network.Legion.diskPersistence' if you need to get started quickly. -}-data Persistence i o s = Persistence {- getState :: PartitionKey -> IO (Maybe (PartitionPowerState i o s)),- saveState :: PartitionKey -> Maybe (PartitionPowerState i o s) -> IO (),- list :: Source IO (PartitionKey, PartitionPowerState i o s)+data Persistence e o s = Persistence {+ getState :: PartitionKey -> IO (Maybe (PartitionPowerState e o s)),+ saveState :: PartitionKey -> Maybe (PartitionPowerState e o s) -> IO (),+ list :: Source IO (PartitionKey, PartitionPowerState e o s) {- ^ List all the keys known to the persistence layer. It is important that the implementation do the right thing with regard to
src/Network/Legion/Basics.hs view
@@ -33,7 +33,7 @@ A convenient memory-based persistence layer. Good for testing or for applications (like caches) that don't have durability requirements. -}-newMemoryPersistence :: IO (Persistence i o s)+newMemoryPersistence :: IO (Persistence e o s) newMemoryPersistence = do cacheT <- atomically (newTVar Map.empty)@@ -44,9 +44,9 @@ } where saveState_- :: TVar (Map PartitionKey (PartitionPowerState i o s))+ :: TVar (Map PartitionKey (PartitionPowerState e o s)) -> PartitionKey- -> Maybe (PartitionPowerState i o s)+ -> Maybe (PartitionPowerState e o s) -> IO () saveState_ cacheT key (Just state) = (atomically . modifyTVar cacheT . insert key) state@@ -55,24 +55,24 @@ (atomically . modifyTVar cacheT . Map.delete) key fetchState- :: TVar (Map PartitionKey (PartitionPowerState i o s))+ :: TVar (Map PartitionKey (PartitionPowerState e o s)) -> PartitionKey- -> IO (Maybe (PartitionPowerState i o s))+ -> IO (Maybe (PartitionPowerState e o s)) fetchState cacheT key = atomically $ lookup key <$> readTVar cacheT list_- :: TVar (Map PartitionKey (PartitionPowerState i o s))- -> Source IO (PartitionKey, PartitionPowerState i o s)+ :: TVar (Map PartitionKey (PartitionPowerState e o s))+ -> Source IO (PartitionKey, PartitionPowerState e o s) list_ cacheT = sourceList . Map.toList =<< lift (atomically (readTVar cacheT)) {- | A convenient way to persist partition states to disk. -}-diskPersistence :: (Binary i, Binary s)+diskPersistence :: (Binary e, Binary s) => FilePath -- ^ The directory under which partition states will be stored.- -> Persistence i o s+ -> Persistence e o s diskPersistence directory = Persistence { getState,@@ -80,18 +80,18 @@ list } where- getState :: (Binary i, Binary s)+ getState :: (Binary e, Binary s) => PartitionKey- -> IO (Maybe (PartitionPowerState i o s))+ -> IO (Maybe (PartitionPowerState e o s)) getState key = let path = toPath key in doesFileExist path >>= bool (return Nothing) ((Just . decode . fromStrict) <$> readFile path) - saveState :: (Binary i, Binary s)+ saveState :: (Binary e, Binary s) => PartitionKey- -> Maybe (PartitionPowerState i o s)+ -> Maybe (PartitionPowerState e o s) -> IO () saveState key (Just state) = writeFile (toPath key) (toStrict (encode state))@@ -101,8 +101,8 @@ (return ()) (removeFile path) - list :: (Binary i, Binary s)- => Source IO (PartitionKey, PartitionPowerState i o s)+ list :: (Binary e, Binary s)+ => Source IO (PartitionKey, PartitionPowerState e o s) list = do keys <- lift $ readHexList <$> getDirectoryContents directory sourceList keys =$= fillData
src/Network/Legion/ClusterState.hs view
@@ -37,7 +37,7 @@ import Network.Legion.Distribution (ParticipationDefaults, modify, Peer) import Network.Legion.KeySet (KeySet, full, unions) import Network.Legion.PartitionKey (PartitionKey)-import Network.Legion.PowerState (ApplyDelta(apply))+import Network.Legion.PowerState (Event(apply)) import Network.Legion.Propagation (PropState, PropPowerState) import Network.Socket (SockAddr) import qualified Data.Map as Map@@ -97,7 +97,7 @@ | PeerEjected Peer deriving (Show, Generic) instance Binary Update-instance ApplyDelta Update () ClusterState where+instance Event Update () ClusterState where apply (PeerJoined peer addr) cs@ClusterState {peers} = ((), cs {peers = Map.insert peer addr peers}) apply (Participating peer ks) cs@ClusterState {distribution} =@@ -119,7 +119,7 @@ -> ClusterPropState claimParticipation peer ks = ClusterPropState- . P.delta (Participating peer ks)+ . P.event (Participating peer ks) . unPropState @@ -130,7 +130,7 @@ new clusterId self addy = claimParticipation self full . ClusterPropState- . P.delta (PeerJoined self (BSockAddr addy))+ . P.event (PeerJoined self (BSockAddr addy)) $ P.new clusterId self (Set.singleton self) @@ -185,7 +185,7 @@ -> ClusterPropState joinCluster peer addy = ClusterPropState- . P.delta (PeerJoined peer addy)+ . P.event (PeerJoined peer addy) . P.participate peer . unPropState @@ -196,7 +196,7 @@ eject :: Peer -> ClusterPropState -> ClusterPropState eject peer = ClusterPropState- . P.delta (PeerEjected peer)+ . P.event (PeerEjected peer) . P.disassociate peer . unPropState
src/Network/Legion/Distribution.hs view
@@ -163,9 +163,7 @@ -{- |- The actions that are taken in order to build a balanced cluster.--}+{- | The actions that are taken in order to build a balanced cluster. -} data RebalanceAction = Invite KeySet deriving (Show, Generic)
src/Network/Legion/PartitionState.hs view
@@ -13,7 +13,7 @@ initProp, participating, getPowerState,- delta,+ event, heartbeat, participate, projParticipants,@@ -29,7 +29,7 @@ import Data.Time.Clock (UTCTime) import Network.Legion.Distribution (Peer) import Network.Legion.PartitionKey (PartitionKey)-import Network.Legion.PowerState (ApplyDelta)+import Network.Legion.PowerState (Event) import Network.Legion.Propagation (PropState, PropPowerState) import qualified Network.Legion.Propagation as P @@ -43,8 +43,8 @@ You can save these guys to disk in your `Network.Legion.Persistence` layer by using its `Binary` instance. -}-newtype PartitionPowerState i o s = PartitionPowerState {- unPowerState :: PropPowerState PartitionKey s Peer i o+newtype PartitionPowerState e o s = PartitionPowerState {+ unPowerState :: PropPowerState PartitionKey s Peer e o } deriving (Show, Binary) @@ -52,8 +52,8 @@ A reification of `PropState`, representing the propagation state of the partition state. -}-newtype PartitionPropState i o s = PartitionPropState {- unPropState :: PropState PartitionKey s Peer i o+newtype PartitionPropState e o s = PartitionPropState {+ unPropState :: PropState PartitionKey s Peer e o } deriving (Eq, Show, ToJSON) @@ -66,18 +66,18 @@ {- | Get the projected partition state value. -}-ask :: (ApplyDelta i o s) => PartitionPropState i o s -> s+ask :: (Event e o s) => PartitionPropState e o s -> s ask = P.ask . unPropState {- | Try to merge two partition states. -}-mergeEither :: (Show i, Show s, ApplyDelta i o s)+mergeEither :: (Show e, Show s, Event e o s) => Peer- -> PartitionPowerState i o s- -> PartitionPropState i o s- -> Either String (PartitionPropState i o s)+ -> PartitionPowerState e o s+ -> PartitionPropState e o s+ -> Either String (PartitionPropState e o s) mergeEither peer ps prop = PartitionPropState <$> P.mergeEither peer (unPowerState ps) (unPropState prop)@@ -89,8 +89,8 @@ state that is applicable after those actions have been taken. -} actions- :: PartitionPropState i o s- -> (Set Peer, PartitionPowerState i o s, PartitionPropState i o s)+ :: PartitionPropState e o s+ -> (Set Peer, PartitionPowerState e o s, PartitionPropState e o s) actions prop = let (peers, ps, newProp) = P.actions (unPropState prop) in (peers, PartitionPowerState ps, PartitionPropState newProp)@@ -106,7 +106,7 @@ {- ^ self -} -> Set Peer {- ^ The default participation. -}- -> PartitionPropState i o s+ -> PartitionPropState e o s new key self = PartitionPropState . P.new key self @@ -114,10 +114,10 @@ Initialize a `PartitionPropState` based on the initial underlying partition power state. -}-initProp :: (ApplyDelta i o s)+initProp :: (Event e o s) => Peer- -> PartitionPowerState i o s- -> PartitionPropState i o s+ -> PartitionPowerState e o s+ -> PartitionPropState e o s initProp self = PartitionPropState . P.initProp self . unPowerState @@ -125,7 +125,7 @@ Return `True` if the local peer is participating in the partition power state. -}-participating :: PartitionPropState i o s -> Bool+participating :: PartitionPropState e o s -> Bool participating = P.participating . unPropState @@ -133,30 +133,30 @@ Get an opaque encapsulation of the partition power state, for transferring accros the network or whatever. -}-getPowerState :: PartitionPropState i o s -> PartitionPowerState i o s+getPowerState :: PartitionPropState e o s -> PartitionPowerState e o s getPowerState = PartitionPowerState . P.getPowerState . unPropState -{- | Apply a delta to the partition state. -}-delta :: (ApplyDelta i o s)- => i- -> PartitionPropState i o s- -> PartitionPropState i o s-delta d = PartitionPropState . P.delta d . unPropState+{- | Apply an event to the partition state. -}+event :: (Event e o s)+ => e+ -> PartitionPropState e o s+ -> PartitionPropState e o s+event d = PartitionPropState . P.event d . unPropState {- | Move time forward for the propagation state. -}-heartbeat :: UTCTime -> PartitionPropState i o s -> PartitionPropState i o s+heartbeat :: UTCTime -> PartitionPropState e o s -> PartitionPropState e o s heartbeat now = PartitionPropState . P.heartbeat now . unPropState {- | Allow a participant to join in the distributed nature of the power state. -}-participate :: (ApplyDelta i o s)+participate :: (Event e o s) => Peer- -> PartitionPropState i o s- -> PartitionPropState i o s+ -> PartitionPropState e o s+ -> PartitionPropState e o s participate peer = PartitionPropState . P.participate peer . unPropState @@ -164,31 +164,31 @@ Return the projected peers which are participating in the partition state. -}-projParticipants :: PartitionPropState i o s -> Set Peer+projParticipants :: PartitionPropState e o s -> Set Peer projParticipants = P.projParticipants . unPropState {- | Get the projected value of a `PartitionPowerState`. -}-projected :: (ApplyDelta i o s) => PartitionPowerState i o s -> s+projected :: (Event e o s) => PartitionPowerState e o s -> s projected = P.projected . unPowerState {- | Get the infimum value of a `PartitionPowerState` -}-infimum :: PartitionPowerState i o s -> s+infimum :: PartitionPowerState e o s -> s infimum = P.infimum . unPowerState {- | Figure out if this propagation state has any work to do. Return 'True' if all known propagation work has been completed. The implication here is that the- only way more work can happen is if new deltas are applied, either directly+ only way more work can happen is if new events are applied, either directly or via a merge. -}-idle :: PartitionPropState i o s -> Bool+idle :: PartitionPropState e o s -> Bool idle = P.idle . unPropState
src/Network/Legion/PowerState.hs view
@@ -7,7 +7,7 @@ module Network.Legion.PowerState ( PowerState, Infimum(..),- ApplyDelta(..),+ Event(..), StateId, new, merge,@@ -23,7 +23,7 @@ projParticipants, divergent, divergences,- delta,+ event, ) where import Prelude hiding (null)@@ -43,21 +43,23 @@ {- | This represents the set of all possible future values of @s@, in a- distributed, monotonically increasing environment.+ distributed, monotonically increasing environment. The term "power+ state" is chosen to indicate that values of this type represent multiple+ possible values of the underlying user state @s@. -}-data PowerState o s p d r = PowerState {+data PowerState o s p e r = PowerState { origin :: o, infimum :: Infimum s p,- deltas :: Map (StateId p) (Delta p d, Set p)+ events :: Map (StateId p) (Delta p e, Set p) } deriving (Generic, Show, Eq)-instance (Binary o, Binary s, Binary p, Binary d) => Binary (PowerState o s p d r)-instance (Show o, Show s, Show p, Show d) => ToJSON (PowerState o s p d r) where- toJSON PowerState {origin, infimum, deltas} = object [+instance (Binary o, Binary s, Binary p, Binary e) => Binary (PowerState o s p e r)+instance (Show o, Show s, Show p, Show e) => ToJSON (PowerState o s p e r) where+ toJSON PowerState {origin, infimum, events} = object [ "origin" .= show origin, "infimum" .= infimum,- "deltas" .= Map.fromList [- (show sid, (show d, Set.map show ps))- | (sid, (d, ps)) <- Map.toList deltas+ "events" .= Map.fromList [+ (show sid, (show e, Set.map show ps))+ | (sid, (e, ps)) <- Map.toList events ] ] @@ -113,28 +115,28 @@ {- | `Delta` is how we represent mutations to the power state. -}-data Delta p d+data Delta p e = Join p | UnJoin p- | Delta d+ | Event e deriving (Generic, Show, Eq)-instance (Binary p, Binary d) => Binary (Delta p d)+instance (Binary p, Binary e) => Binary (Delta p e) {- |- The class which allows for delta application.+ The class which allows for event application. -}-class ApplyDelta i o s | i s -> o where+class Event e o s | e -> s o where {- |- Apply a delta to a state value. *This function MUST be total!!!*+ Apply an event to a state value. *This function MUST be total!!!* -}- apply :: i -> s -> (o, s)+ apply :: e -> s -> (o, s) {- | Construct a new PowerState with the given origin and initial participants -}-new :: (Default s) => o -> Set p -> PowerState o s p d r+new :: (Default s) => o -> Set p -> PowerState o s p e r new origin participants = PowerState { origin,@@ -143,7 +145,7 @@ participants, stateValue = def },- deltas = Map.empty+ events = Map.empty } @@ -153,10 +155,10 @@ a lower one. This function is not total. Only `PowerState`s that originated from the same `new` call can be merged. -}-merge :: (Eq o, ApplyDelta d r s, Ord p, Show o, Show s, Show p, Show d)- => PowerState o s p d r- -> PowerState o s p d r- -> PowerState o s p d r+merge :: (Eq o, Event e r s, Ord p, Show o, Show s, Show p, Show e)+ => PowerState o s p e r+ -> PowerState o s p e r+ -> PowerState o s p e r merge a b = either error id (mergeEither a b) @@ -164,10 +166,10 @@ Like `merge`, but safe. Returns `Nothing` if the two power states do not share the same origin. -}-mergeMaybe :: (Eq o, ApplyDelta d r s, Ord p, Show o, Show s, Show p, Show d)- => PowerState o s p d r- -> PowerState o s p d r- -> Maybe (PowerState o s p d r)+mergeMaybe :: (Eq o, Event e r s, Ord p, Show o, Show s, Show p, Show e)+ => PowerState o s p e r+ -> PowerState o s p e r+ -> Maybe (PowerState o s p e r) mergeMaybe a b = either (const Nothing) Just (mergeEither a b) @@ -175,27 +177,27 @@ Like `mergeMaybe`, but returns a human-decipherable error message of exactly what went wrong. -}-mergeEither :: (Eq o, ApplyDelta d r s, Ord p, Show o, Show s, Show p, Show d)- => PowerState o s p d r- -> PowerState o s p d r- -> Either String (PowerState o s p d r)+mergeEither :: (Eq o, Event e r s, Ord p, Show o, Show s, Show p, Show e)+ => PowerState o s p e r+ -> PowerState o s p e r+ -> Either String (PowerState o s p e r) mergeEither (PowerState o1 i1 d1) (PowerState o2 i2 d2) | o1 == o2 = Right . reduce . removeRenegade $ PowerState { origin = o1, infimum,- deltas = removeObsolete (unionWith mergeAcks d1 d2)+ events = removeObsolete (unionWith mergeAcks d1 d2) } where infimum = max i1 i2 {- |- Obsolete deltas are deltas that are already included in the latest+ Obsolete events are events that are already included in the latest infimum. -} removeObsolete = filterWithKey (\k _ -> k > stateId infimum) {- |- Renegade deltas are deltas that originate from a non-participating+ Renegade events are events that originate from a non-participating peer. This might happen in a network partition situation, where the cluster ejected a peer that later reappears on the network, broadcasting updates.@@ -209,11 +211,11 @@ -} removeRenegade ps = ps {- deltas =+ events = fromAscList . filter nonRenegade . toAscList- . deltas+ . events $ ps } where@@ -221,7 +223,7 @@ nonRenegade (Sid _ p, _) = p `member` peers peers = allParticipants ps - mergeAcks (d, s1) (_, s2) = (d, s1 `union` s2)+ mergeAcks (e, s1) (_, s2) = (e, s1 `union` s2) mergeEither a b = Left $ "PowerStates " ++ show a ++ " and " ++ show b ++ " do not share the "@@ -233,25 +235,25 @@ contained in the powerset. The implication is that the participant __must__ base all future operations on the result of this function. -}-acknowledge :: (ApplyDelta d r s, Ord p)+acknowledge :: (Event e r s, Ord p) => p- -> PowerState o s p d r- -> PowerState o s p d r-acknowledge p ps@PowerState {deltas} =- reduce ps {deltas = fmap ackOne deltas}+ -> PowerState o s p e r+ -> PowerState o s p e r+acknowledge p ps@PowerState {events} =+ reduce ps {events = fmap ackOne events} where- ackOne (d, acks) = (d, Set.insert p acks)+ ackOne (e, acks) = (e, Set.insert p acks) {- | Allow a participant to join in the distributed nature of the power state. -}-participate :: (ApplyDelta d r s, Ord p)+participate :: (Event e r s, Ord p) => p- -> PowerState o s p d r- -> PowerState o s p d r-participate p ps@PowerState {deltas} = acknowledge p $ ps {- deltas = Map.insert (nextId p ps) (Join p, Set.empty) deltas+ -> PowerState o s p e r+ -> PowerState o s p e r+participate p ps@PowerState {events} = acknowledge p $ ps {+ events = Map.insert (nextId p ps) (Join p, Set.empty) events } @@ -259,51 +261,51 @@ Indicate that a participant is removing itself from participating in the distributed power state. -}-disassociate :: (ApplyDelta d r s, Ord p)+disassociate :: (Event e r s, Ord p) => p- -> PowerState o s p d r- -> PowerState o s p d r-disassociate p ps@PowerState {deltas} = acknowledge p $ ps {- deltas = Map.insert (nextId p ps) (UnJoin p, Set.empty) deltas+ -> PowerState o s p e r+ -> PowerState o s p e r+disassociate p ps@PowerState {events} = acknowledge p $ ps {+ events = Map.insert (nextId p ps) (UnJoin p, Set.empty) events } {- | Introduce a change to the PowerState on behalf of the participant. -}-delta :: (ApplyDelta d r s, Ord p)+event :: (Event e r s, Ord p) => p- -> d- -> PowerState o s p d r- -> PowerState o s p d r-delta p d ps@PowerState {deltas} = acknowledge p $ ps {- deltas = Map.insert (nextId p ps) (Delta d, Set.empty) deltas+ -> e+ -> PowerState o s p e r+ -> PowerState o s p e r+event p e ps@PowerState {events} = acknowledge p $ ps {+ events = Map.insert (nextId p ps) (Event e, Set.empty) events } {- | Return the current projected value of the power state. -}-projectedValue :: (ApplyDelta d r s) => PowerState o s p d r -> s-projectedValue PowerState {infimum = Infimum {stateValue}, deltas} =- foldr (\ i s -> snd (apply i s)) stateValue changes+projectedValue :: (Event e r s) => PowerState o s p e r -> s+projectedValue PowerState {infimum = Infimum {stateValue}, events} =+ foldr (\ e s -> snd (apply e s)) stateValue changes where- changes = foldr getDeltas [] (toDescList deltas)- getDeltas (_, (Delta d, _)) acc = d:acc+ changes = foldr getDeltas [] (toDescList events)+ getDeltas (_, (Event e, _)) acc = e:acc getDeltas _ acc = acc {- | Return the current infimum value of the power state. -}-infimumValue :: PowerState o s p d r -> s+infimumValue :: PowerState o s p e r -> s infimumValue PowerState {infimum = Infimum {stateValue}} = stateValue {- | Gets the known participants at the infimum. -}-infimumParticipants :: PowerState o s p d r -> Set p+infimumParticipants :: PowerState o s p e r -> Set p infimumParticipants PowerState {infimum = Infimum {participants}} = participants @@ -311,12 +313,12 @@ Get all known participants. This includes participants that are projected for removal. -}-allParticipants :: (Ord p) => PowerState o s p d r -> Set p+allParticipants :: (Ord p) => PowerState o s p e r -> Set p allParticipants PowerState { infimum = Infimum {participants},- deltas+ events } =- foldr updateParticipants participants (toDescList deltas)+ foldr updateParticipants participants (toDescList events) where updateParticipants (_, (Join p, _)) = Set.insert p updateParticipants _ = id@@ -326,12 +328,12 @@ Get all the projected participants. This does not include participants that are projected for removal. -}-projParticipants :: (Ord p) => PowerState o s p d r -> Set p+projParticipants :: (Ord p) => PowerState o s p e r -> Set p projParticipants PowerState { infimum = Infimum {participants},- deltas+ events } =- foldr updateParticipants participants (toDescList deltas)+ foldr updateParticipants participants (toDescList events) where updateParticipants (_, (Join p, _)) = Set.insert p updateParticipants (_, (UnJoin p, _)) = Set.delete p@@ -340,15 +342,15 @@ {- | Returns the participants that we think might be diverging. In this- context, a peer is "diverging" if there is a delta that the peer has+ context, a peer is "diverging" if there is an event that the peer has not acknowledged. -}-divergent :: (Ord p) => PowerState o s p d r -> Set p+divergent :: (Ord p) => PowerState o s p e r -> Set p divergent PowerState { infimum = Infimum {participants},- deltas+ events } =- accum participants Set.empty (toAscList deltas)+ accum participants Set.empty (toAscList events) where {- | `accum` mnemonics:@@ -373,7 +375,7 @@ in accum j2 d2 moreDeltas - accum j d ((_, (Delta _, a)):moreDeltas) =+ accum j d ((_, (Event _, a)):moreDeltas) = let d2 = (j \\ a) `union` d in@@ -381,13 +383,13 @@ {- |- Return the deltas that are unknown to the specified peer.+ Return the events that are unknown to the specified peer. -}-divergences :: (Ord p) => p -> PowerState o s p d r -> Map (StateId p) d-divergences peer PowerState {deltas} =+divergences :: (Ord p) => p -> PowerState o s p e r -> Map (StateId p) e+divergences peer PowerState {events} = fromAscList [- (sid, d)- | (sid, (Delta d, p)) <- toAscList deltas+ (sid, e)+ | (sid, (Event e, p)) <- toAscList events , not (peer `member` p) ] @@ -397,37 +399,37 @@ has enough information to derive a new infimum value. In other words, this is where garbage collection happens. -}-reduce :: (ApplyDelta d r s, Ord p) => PowerState o s p d r -> PowerState o s p d r+reduce :: (Event e r s, Ord p) => PowerState o s p e r -> PowerState o s p e r reduce ps@PowerState { infimum = infimum@Infimum {participants, stateValue},- deltas+ events } =- case minViewWithKey deltas of+ case minViewWithKey events of Nothing -> ps- Just ((i, (update, acks)), newDeltas) ->+ Just ((sid, (update, acks)), newDeltas) -> if not . null $ participants \\ acks then ps else case update of Join p -> reduce ps { infimum = infimum {- stateId = i,+ stateId = sid, participants = Set.insert p participants },- deltas = newDeltas+ events = newDeltas } UnJoin p -> reduce ps { infimum = infimum {- stateId = i,+ stateId = sid, participants = Set.delete p participants },- deltas = newDeltas+ events = newDeltas }- Delta d -> reduce ps {+ Event e -> reduce ps { infimum = infimum {- stateId = i,- stateValue = snd (apply d stateValue)+ stateId = sid,+ stateValue = snd (apply e stateValue) },- deltas = newDeltas+ events = newDeltas } @@ -435,9 +437,9 @@ A utility function that constructs the next `StateId` on behalf of a participant. -}-nextId :: (Ord p) => p -> PowerState o s p d r -> StateId p-nextId p PowerState {infimum = Infimum {stateId}, deltas} =- case maximum (stateId:keys deltas) of+nextId :: (Ord p) => p -> PowerState o s p e r -> StateId p+nextId p PowerState {infimum = Infimum {stateId}, events} =+ case maximum (stateId:keys events) of BottomSid -> Sid 0 p Sid ord _ -> Sid (succ ord) p
src/Network/Legion/Propagation.hs view
@@ -12,7 +12,7 @@ mergeMaybe, mergeEither, heartbeat,- delta,+ event, actions, new, initProp,@@ -40,7 +40,7 @@ import Data.Set (member, Set) import Data.Time.Clock (NominalDiffTime, UTCTime, addUTCTime) import Data.Time.Format () -- For `instance Show UTCTime`-import Network.Legion.PowerState (PowerState, divergent, ApplyDelta,+import Network.Legion.PowerState (PowerState, divergent, Event, acknowledge, projectedValue, StateId) import qualified Data.Map as Map import qualified Data.Set as Set@@ -97,14 +97,14 @@ {- | Retriev the current projected value of the underlying state. -}-ask :: (ApplyDelta d r s) => PropState o s p d r -> s+ask :: (Event d r s) => PropState o s p d r -> s ask = projectedValue . powerState {- | Create a new propagation state based on an existing power state. -}-initProp :: (ApplyDelta d r s, Ord p)+initProp :: (Event d r s, Ord p) => p -> PropPowerState o s p d r -> PropState o s p d r@@ -155,7 +155,7 @@ Like `merge`, but total. `mergeEither` returns a human readable reason why the foreign powerstate can't be merged in the event of an error. -}-mergeEither :: (Eq o, Ord p, Show o, Show s, Show p, Show d, ApplyDelta d r s)+mergeEither :: (Eq o, Ord p, Show o, Show s, Show p, Show d, Event d r s) => p -> PropPowerState o s p d r -> PropState o s p d r@@ -191,7 +191,7 @@ Like `merge`, but total. `mergeMaybe` returns `Nothing` if the foreign power state can't be merged. -}-mergeMaybe :: (Eq o, Ord p, Show o, Show s, Show p, Show d, ApplyDelta d r s)+mergeMaybe :: (Eq o, Ord p, Show o, Show s, Show p, Show d, Event d r s) => p -> PropPowerState o s p d r -> PropState o s p d r@@ -208,7 +208,7 @@ precondition is not met, `error` will be called (making this function non-total). Using `mergeMaybe` or `mergeEither` is recommended. -}-merge :: (Eq o, Ord p, Show o, Show s, Show p, Show d, ApplyDelta d r s)+merge :: (Eq o, Ord p, Show o, Show s, Show p, Show d, Event d r s) => p -> PropPowerState o s p d r -> PropState o s p d r@@ -227,14 +227,14 @@ {- |- Apply a delta.+ Apply an event. -}-delta :: (Ord p, ApplyDelta d r s)+event :: (Ord p, Event d r s) => d -> PropState o s p d r -> PropState o s p d r-delta d prop@PropState {self, powerState, now} =- let newPowerState = PS.delta self d powerState+event d prop@PropState {self, powerState, now} =+ let newPowerState = PS.event self d powerState in prop { powerState = newPowerState, peerStates = Map.fromAscList [@@ -292,7 +292,7 @@ {- | Allow a participant to join in the distributed nature of the power state. -}-participate :: (Ord p, ApplyDelta d r s)+participate :: (Ord p, Event d r s) => p -> PropState o s p d r -> PropState o s p d r@@ -310,7 +310,7 @@ {- | Eject a participant from the power state. -}-disassociate :: (Ord p, ApplyDelta d r s)+disassociate :: (Ord p, Event d r s) => p -> PropState o s p d r -> PropState o s p d r@@ -326,7 +326,7 @@ {- |- Return the deltas that are unknown to the specified peer.+ Return the events that are unknown to the specified peer. -} divergences :: (Ord p) => p -> PropState o s p d r -> Map (StateId p) d divergences peer = PS.divergences peer . powerState@@ -368,7 +368,7 @@ {- | Get the projected value of a PropPowerState. -}-projected :: (ApplyDelta d r s) => PropPowerState o s p d r -> s+projected :: (Event d r s) => PropPowerState o s p d r -> s projected = PS.projectedValue . unPowerState @@ -382,7 +382,7 @@ {- | Figure out if this propagation state has any work to do. Return 'True' if all known propagation work has been completed. The implication here is that the- only way more work can happen is if new deltas are applied, either directly+ only way more work can happen is if new events are applied, either directly or via a merge. -} idle :: (Ord p) => PropState o s p d r -> Bool
src/Network/Legion/Runtime.hs view
@@ -82,13 +82,13 @@ request source, and to handle the responses. Unless you know exactly what you are doing, you probably want to use `forkLegionary` instead. -}-runLegionary :: (LegionConstraints i o s)- => Persistence i o s+runLegionary :: (LegionConstraints e o s)+ => Persistence e o s {- ^ The persistence layer used to back the legion framework. -} -> RuntimeSettings {- ^ Settings and configuration of the legionframework. -} -> StartupMode- -> Source IO (RequestMsg i o)+ -> Source IO (RequestMsg e o) {- ^ A source of requests, together with a way to respond to the requets. -} -> LoggingT IO () {-@@ -128,11 +128,11 @@ :: Either (JoinRequest, JoinResponse -> LIO ()) (Either- (PeerMessage i o s)+ (PeerMessage e o s) (Either- (RequestMsg i o)- (AdminMessage i o s)))- -> RuntimeMessage i o s+ (RequestMsg e o)+ (AdminMessage e o s)))+ -> RuntimeMessage e o s toMessage (Left m) = J m toMessage (Right (Left m)) = P m toMessage (Right (Right (Left m))) = R m@@ -142,7 +142,7 @@ Turn an LIO-based conduit into an IO-based conduit, so that it will work with `merge`. -}- loggingC :: ConduitM i o LIO r -> LIO (ConduitM i o IO r)+ loggingC :: ConduitM e o LIO r -> LIO (ConduitM e o IO r) loggingC c = do logging <- askLoggerIO return (transPipe (`runLoggingT` logging) c)@@ -154,18 +154,18 @@ which the request is directed, and a way for the framework to deliver the response to some interested party. -}-data RequestMsg i o- = Request PartitionKey i (o -> IO ())+data RequestMsg e o+ = Request PartitionKey e (o -> IO ()) | SearchDispatch SearchTag (Maybe IndexRecord -> IO ())-instance (Show i) => Show (RequestMsg i o) where- show (Request k i _) = "(Request " ++ show k ++ " " ++ show i ++ " _)"+instance (Show e) => Show (RequestMsg e o) where+ show (Request k e _) = "(Request " ++ show k ++ " " ++ show e ++ " _)" show (SearchDispatch s _) = "(SearchDispatch " ++ show s ++ " _)" -messageSink :: (LegionConstraints i o s)- => Persistence i o s- -> (RuntimeState i o s, NodeState i o s)- -> Sink (RuntimeMessage i o s) LIO ()+messageSink :: (LegionConstraints e o s)+ => Persistence e o s+ -> (RuntimeState e o s, NodeState e o s)+ -> Sink (RuntimeMessage e o s) LIO () messageSink persistence states = await >>= \case Nothing -> return ()@@ -184,9 +184,9 @@ joined the cluster. -} updatePeers- :: Persistence i o s- -> (RuntimeState i o s, NodeState i o s)- -> LIO (RuntimeState i o s, NodeState i o s)+ :: Persistence e o s+ -> (RuntimeState e o s, NodeState e o s)+ -> LIO (RuntimeState e o s, NodeState e o s) updatePeers persistence (rts, ns) = do (peers, ns2) <- runSM persistence ns SM.getPeers newPeers (cm rts) peers@@ -197,10 +197,10 @@ Perform any cluster management actions, and update the state appropriately. -}-clusterHousekeeping :: (LegionConstraints i o s)- => Persistence i o s- -> (RuntimeState i o s, NodeState i o s)- -> LIO (RuntimeState i o s, NodeState i o s)+clusterHousekeeping :: (LegionConstraints e o s)+ => Persistence e o s+ -> (RuntimeState e o s, NodeState e o s)+ -> LIO (RuntimeState e o s, NodeState e o s) clusterHousekeeping persistence (rts, ns) = do (actions, ns2) <- runSM persistence ns ( heartbeat@@ -217,9 +217,9 @@ machine. -} clusterAction- :: ClusterAction i o s- -> RuntimeState i o s- -> LIO (RuntimeState i o s)+ :: ClusterAction e o s+ -> RuntimeState e o s+ -> LIO (RuntimeState e o s) clusterAction (SM.ClusterMerge peer ps)@@ -241,11 +241,11 @@ state and an initial node state, and producing an updated runtime state and node state. -}-handleMessage :: (LegionConstraints i o s)- => Persistence i o s- -> RuntimeMessage i o s- -> (RuntimeState i o s, NodeState i o s)- -> LIO (RuntimeState i o s, NodeState i o s)+handleMessage :: (LegionConstraints e o s)+ => Persistence e o s+ -> RuntimeMessage e o s+ -> (RuntimeState e o s, NodeState e o s)+ -> LIO (RuntimeState e o s, NodeState e o s) handleMessage {- Partition Merge -} persistence@@ -355,7 +355,7 @@ ns ) where- sendOne :: Peer -> RuntimeState i o s -> LIO (RuntimeState i o s)+ sendOne :: Peer -> RuntimeState e o s -> LIO (RuntimeState e o s) sendOne peer r@RuntimeState {nextId} = do send cm peer (PeerMessage self nextId (Search searchTag)) return r {nextId = nextMessageId nextId}@@ -509,9 +509,9 @@ is why this is an @LIO (Source LIO PeerMessage)@ instead of a @Source LIO PeerMessage@. -}-startPeerListener :: (LegionConstraints i o s)+startPeerListener :: (LegionConstraints e o s) => RuntimeSettings- -> LIO (Source LIO (PeerMessage i o s))+ -> LIO (Source LIO (PeerMessage e o s)) startPeerListener RuntimeSettings {peerBindAddr} = catchAll (do@@ -531,9 +531,9 @@ throwM err ) where- acceptLoop :: (LegionConstraints i o s)+ acceptLoop :: (LegionConstraints e o s) => Socket- -> Chan (PeerMessage i o s)+ -> Chan (PeerMessage e o s) -> LIO () acceptLoop so inputChan = catchAll (@@ -575,7 +575,7 @@ makeNodeState :: RuntimeSettings -> StartupMode- -> LIO (Peer, NodeState i o s, Map Peer BSockAddr)+ -> LIO (Peer, NodeState e o s, Map Peer BSockAddr) makeNodeState RuntimeSettings {peerBindAddr} NewCluster = do {- Build a brand new node state, for the first node in a cluster. -}@@ -696,21 +696,21 @@ Forks the legion framework in a background thread, and returns a way to send user requests to it and retrieve the responses to those requests. - - @__i__@ is the type of request your application will handle. @__i__@ stands- for __"input"__.+ - @__e__@ is the type of request your application will handle. @__e__@ stands+ for __"event"__. - @__o__@ is the type of response produced by your application. @__o__@ stands for __"output"__ - @__s__@ is the type of state maintained by your application. More precisely, it is the type of the individual partitions that make up your global application state. @__s__@ stands for __"state"__. -}-forkLegionary :: (LegionConstraints i o s, MonadLoggerIO io)- => Persistence i o s+forkLegionary :: (LegionConstraints e o s, MonadLoggerIO io)+ => Persistence e o s {- ^ The persistence layer used to back the legion framework. -} -> RuntimeSettings {- ^ Settings and configuration of the legion framework. -} -> StartupMode- -> io (Runtime i o)+ -> io (Runtime e o) forkLegionary persistence settings startupMode = do logging <- askLoggerIO@@ -742,12 +742,12 @@ 'Runtime' is an opaque structure. Use 'makeRequest' to access it. -}-data Runtime i o = Runtime {+data Runtime e o = Runtime { {- | Send an application request to the legion runtime, and get back a response. -}- rtMakeRequest :: PartitionKey -> i -> IO o,+ rtMakeRequest :: PartitionKey -> e -> IO o, {- | Query the index to find a set of partition keys. -} rtSearch :: SearchTag -> IO (Maybe IndexRecord)@@ -755,7 +755,7 @@ {- | Send a user request to the legion runtime. -}-makeRequest :: (MonadIO io) => Runtime i o -> PartitionKey -> i -> io o+makeRequest :: (MonadIO io) => Runtime e o -> PartitionKey -> e -> io o makeRequest rt key = liftIO . rtMakeRequest rt key @@ -763,7 +763,7 @@ Send a search request to the legion runtime. Returns results that are __strictly greater than__ the provided 'SearchTag'. -}-search :: (MonadIO io) => Runtime i o -> SearchTag -> Source io IndexRecord+search :: (MonadIO io) => Runtime e o -> SearchTag -> Source io IndexRecord search rt tag = liftIO (rtSearch rt tag) >>= \case Nothing -> return ()@@ -773,12 +773,12 @@ {- | This is the type of message passed around in the runtime. -}-data RuntimeMessage i o s- = P (PeerMessage i o s)- | R (RequestMsg i o)+data RuntimeMessage e o s+ = P (PeerMessage e o s)+ | R (RequestMsg e o) | J (JoinRequest, JoinResponse -> LIO ())- | A (AdminMessage i o s)-instance (Show i, Show o, Show s) => Show (RuntimeMessage i o s) where+ | A (AdminMessage e o s)+instance (Show e, Show o, Show s) => Show (RuntimeMessage e o s) where show (P m) = "(P " ++ show m ++ ")" show (R m) = "(R " ++ show m ++ ")" show (J (jr, _)) = "(J (" ++ show jr ++ ", _))"@@ -807,11 +807,11 @@ the reader. It does help simplify the code a little bit because we don't have to specify some kind of UUID to differentiate otherwise identical searches. -}-data RuntimeState i o s = RuntimeState {+data RuntimeState e o s = RuntimeState { self :: Peer, forwarded :: Map MessageId (o -> LIO ()), nextId :: MessageId,- cm :: ConnectionManager i o s,+ cm :: ConnectionManager e o s, searches :: Map SearchTag (Set Peer, Maybe IndexRecord, [Maybe IndexRecord -> LIO ()])
src/Network/Legion/Runtime/ConnectionManager.hs view
@@ -36,17 +36,17 @@ {- | A handle on the connection manager -}-data ConnectionManager i o s = C (Chan (Message i o s))-instance Show (ConnectionManager i o s) where+data ConnectionManager e o s = C (Chan (Message e o s))+instance Show (ConnectionManager e o s) where show _ = "ConnectionManager" {- | Create a new connection manager. -}-newConnectionManager :: (Binary i, Binary o, Binary s)+newConnectionManager :: (Binary e, Binary o, Binary s) => Map Peer BSockAddr- -> LIO (ConnectionManager i o s)+ -> LIO (ConnectionManager e o s) newConnectionManager initPeers = do chan <- lift newChan forkC "connection manager thread" $@@ -55,16 +55,16 @@ newPeers cm initPeers return cm where- manager :: (Binary s, Binary o, Binary i)- => Chan (Message i o s)- -> State i o s+ manager :: (Binary s, Binary o, Binary e)+ => Chan (Message e o s)+ -> State e o s -> LIO () manager chan state = lift (readChan chan) >>= handle state >>= manager chan - handle :: (Binary i, Binary o, Binary s)- => State i o s- -> Message i o s- -> LIO (State i o s)+ handle :: (Binary e, Binary o, Binary s)+ => State e o s+ -> Message e o s+ -> LIO (State e o s) handle s@S {connections} (NewPeer peer addr) = case lookup peer connections of Nothing -> do@@ -85,9 +85,9 @@ {- | Build a new connection. -}-connection :: (Binary i, Binary o, Binary s)+connection :: (Binary e, Binary o, Binary s) => SockAddr- -> LIO (Chan (PeerMessage i o s))+ -> LIO (Chan (PeerMessage e o s)) connection addr = do chan <- lift newChan@@ -95,8 +95,8 @@ handle chan Nothing return chan where- handle :: (Binary i, Binary o, Binary s)- => Chan (PeerMessage i o s)+ handle :: (Binary e, Binary o, Binary s)+ => Chan (PeerMessage e o s) -> Maybe Socket -> LIO () handle chan so =@@ -152,9 +152,9 @@ Send a message to a peer. -} send- :: ConnectionManager i o s+ :: ConnectionManager e o s -> Peer- -> PeerMessage i o s+ -> PeerMessage e o s -> LIO () send (C chan) peer = lift . writeChan chan . Send peer @@ -163,7 +163,7 @@ Tell the connection manager about a new peer. -} newPeer- :: ConnectionManager i o s+ :: ConnectionManager e o s -> Peer -> SockAddr -> LIO ()@@ -173,7 +173,7 @@ {- | Tell the connection manager about all the peers known to the cluster state. -}-newPeers :: ConnectionManager i o s -> Map Peer BSockAddr -> LIO ()+newPeers :: ConnectionManager e o s -> Map Peer BSockAddr -> LIO () newPeers cm peers = mapM_ oneNewPeer (toList peers) where@@ -183,17 +183,17 @@ {- | The internal state of the connection manager. -}-data State i o s = S {- connections :: Map Peer (Chan (PeerMessage i o s))+data State e o s = S {+ connections :: Map Peer (Chan (PeerMessage e o s)) } {- | The types of messages that the ConnectionManager understands. -}-data Message i o s+data Message e o s = NewPeer Peer SockAddr- | Send Peer (PeerMessage i o s)+ | Send Peer (PeerMessage e o s) {- |
src/Network/Legion/Runtime/PeerMessage.hs view
@@ -28,13 +28,13 @@ {- | The type of messages sent between peers. -}-data PeerMessage i o s = PeerMessage {+data PeerMessage e o s = PeerMessage { source :: Peer, messageId :: MessageId,- payload :: PeerMessagePayload i o s+ payload :: PeerMessagePayload e o s } deriving (Generic, Show)-instance (Binary i, Binary o, Binary s) => Binary (PeerMessage i o s)+instance (Binary e, Binary o, Binary s) => Binary (PeerMessage e o s) {- |@@ -45,15 +45,15 @@ these messages should result in the ejection of that node from the cluster and the blacklisting of that node so that it can never re-join. -}-data PeerMessagePayload i o s- = PartitionMerge PartitionKey (PartitionPowerState i o s)- | ForwardRequest PartitionKey i+data PeerMessagePayload e o s+ = PartitionMerge PartitionKey (PartitionPowerState e o s)+ | ForwardRequest PartitionKey e | ForwardResponse MessageId o | ClusterMerge ClusterPowerState | Search SearchTag | SearchResponse SearchTag (Maybe IndexRecord) deriving (Generic, Show)-instance (Binary i, Binary o, Binary s) => Binary (PeerMessagePayload i o s)+instance (Binary e, Binary o, Binary s) => Binary (PeerMessagePayload e o s) data MessageId = M UUID Word64 deriving (Generic, Show, Eq, Ord)
src/Network/Legion/StateMachine.hs view
@@ -88,7 +88,7 @@ import Network.Legion.LIO (LIO) import Network.Legion.PartitionKey (PartitionKey) import Network.Legion.PartitionState (PartitionPowerState, PartitionPropState)-import Network.Legion.PowerState (ApplyDelta, apply)+import Network.Legion.PowerState (Event, apply) import qualified Data.Conduit.List as CL import qualified Data.Map as Map import qualified Data.Set as Set@@ -102,20 +102,20 @@ This is the portion of the local node state that is not persistence related. -}-data NodeState i o s = NodeState {+data NodeState e o s = NodeState { self :: Peer, cluster :: ClusterPropState,- partitions :: Map PartitionKey (PartitionPropState i o s),+ partitions :: Map PartitionKey (PartitionPropState e o s), migration :: KeySet, nsIndex :: Set IndexRecord }-instance (Show i, Show s) => Show (NodeState i o s) where+instance (Show e, Show s) => Show (NodeState e o s) where show = unpack . decodeUtf8 . toStrict . encode {- The ToJSON instance is mainly for debugging. The Haskell-generated 'Show' instance is very hard to read. -}-instance (Show i, Show s) => ToJSON (NodeState i o s) where+instance (Show e, Show s) => ToJSON (NodeState e o s) where toJSON (NodeState self cluster partitions migration nsIndex) = object [ "self" .= show self,@@ -129,7 +129,7 @@ {- | Make a new node state. -}-newNodeState :: Peer -> ClusterPropState -> NodeState i o s+newNodeState :: Peer -> ClusterPropState -> NodeState e o s newNodeState self cluster = NodeState { self,@@ -150,8 +150,8 @@ if we want to model the global state of the node as a single unit, we have to do so using a monad. -}-newtype SM i o s a = SM {- unSM :: ReaderT (Persistence i o s) (StateT (NodeState i o s) LIO) a+newtype SM e o s a = SM {+ unSM :: ReaderT (Persistence e o s) (StateT (NodeState e o s) LIO) a } deriving (Functor, Applicative, Monad, MonadLogger, MonadIO) @@ -160,18 +160,18 @@ Run an SM action. -} runSM- :: Persistence i o s- -> NodeState i o s- -> SM i o s a- -> LIO (a, NodeState i o s)+ :: Persistence e o s+ -> NodeState e o s+ -> SM e o s a+ -> LIO (a, NodeState e o s) runSM p ns action = runStateT (runReaderT (unSM action) p) ns {- | Handle a user request. -}-userRequest :: (ApplyDelta i o s, Default s, Indexable s)+userRequest :: (Event e o s, Default s, Indexable s) => PartitionKey- -> i- -> SM i o s (UserResponse o)+ -> e+ -> SM e o s (UserResponse o) userRequest key request = SM $ do NodeState {self, cluster} <- lift get let owners = C.findPartition key cluster@@ -180,7 +180,7 @@ partition <- unSM $ getPartition key let response = fst (apply request (P.ask partition))- partition2 = P.delta request partition+ partition2 = P.event request partition unSM $ savePartition key partition2 return (Respond response) @@ -196,11 +196,11 @@ Handle the state transition for a partition merge event. Returns 'Left' if there is an error, and 'Right' if everything went fine. -}-partitionMerge :: (Show i, Show s, ApplyDelta i o s, Default s, Indexable s)+partitionMerge :: (Show e, Show s, Event e o s, Default s, Indexable s) => Peer -> PartitionKey- -> PartitionPowerState i o s- -> SM i o s ()+ -> PartitionPowerState e o s+ -> SM e o s () partitionMerge source key foreignPartition = do partition <- getPartition key case P.mergeEither source foreignPartition partition of@@ -215,7 +215,7 @@ clusterMerge :: Peer -> ClusterPowerState- -> SM i o s ()+ -> SM e o s () clusterMerge source foreignCluster = SM . lift $ do nodeState@NodeState {migration, cluster} <- get case C.mergeEither source foreignCluster cluster of@@ -242,7 +242,7 @@ peer to a partition. This will cause the data to be transfered in the normal course of propagation. -}-migrate :: (Default s, ApplyDelta i o s, Indexable s) => SM i o s ()+migrate :: (Default s, Event e o s, Indexable s) => SM e o s () migrate = do NodeState {migration} <- (SM . lift) get persistence <- SM ask@@ -252,8 +252,8 @@ $$ accum (SM . lift) $ modify (\ns -> ns {migration = KS.empty}) where- accum :: (Default s, ApplyDelta i o s, Indexable s)- => Sink (PartitionKey, PartitionPowerState i o s) (SM i o s) ()+ accum :: (Default s, Event e o s, Indexable s)+ => Sink (PartitionKey, PartitionPowerState e o s) (SM e o s) () accum = awaitForever $ \ (key, ps) -> do NodeState {self, cluster, partitions} <- (lift . SM . lift) get let@@ -268,7 +268,7 @@ Handle all cluster and partition state propagation actions, and return an updated node state. -}-propagate :: SM i o s [ClusterAction i o s]+propagate :: SM e o s [ClusterAction e o s] propagate = SM $ do partitionActions <- getPartitionActions clusterActions <- unSM getClusterActions@@ -296,7 +296,7 @@ } return actions - getClusterActions :: SM i o s [ClusterAction i o s]+ getClusterActions :: SM e o s [ClusterAction e o s] getClusterActions = SM $ do ns@NodeState {cluster} <- lift get let@@ -312,7 +312,7 @@ Figure out if any rebalancing actions must be taken by this node, and kick them off if so. -}-rebalance :: SM i o s ()+rebalance :: SM e o s () rebalance = SM $ do ns@NodeState {self, cluster} <- lift get let@@ -334,7 +334,7 @@ {- | Update all of the propagation states with the current time. -}-heartbeat :: SM i o s ()+heartbeat :: SM e o s () heartbeat = SM $ do now <- lift3 getCurrentTime ns@NodeState {cluster, partitions} <- lift get@@ -348,14 +348,14 @@ {- | Eject a peer from the cluster. -}-eject :: Peer -> SM i o s ()+eject :: Peer -> SM e o s () eject peer = SM . lift $ do ns@NodeState {cluster} <- get put ns {cluster = C.eject peer cluster} {- | Handle a peer join request. -}-join :: BSockAddr -> SM i o s (Peer, ClusterPowerState)+join :: BSockAddr -> SM e o s (Peer, ClusterPowerState) join peerAddr = SM $ do peer <- lift2 newPeer ns@NodeState {cluster} <- lift get@@ -387,7 +387,7 @@ TODO: implement fastest competitive search. -}-minimumCompleteServiceSet :: SM i o s (Set Peer)+minimumCompleteServiceSet :: SM e o s (Set Peer) minimumCompleteServiceSet = SM $ do NodeState {cluster} <- lift get return (D.minimumCompleteServiceSet (C.getDistribution cluster))@@ -397,7 +397,7 @@ Search the index, and return the first record that is __strictly greater than__ the provided search tag, if such a record exists. -}-search :: SearchTag -> SM i o s (Maybe IndexRecord)+search :: SearchTag -> SM e o s (Maybe IndexRecord) search SearchTag {stTag, stKey = Nothing} = SM $ do NodeState {nsIndex} <- lift get return (Set.lookupGE IndexRecord {irTag = stTag, irKey = minBound} nsIndex)@@ -411,9 +411,9 @@ with other nodes. It is up to the runtime system to implement the actions. -}-data ClusterAction i o s+data ClusterAction e o s = ClusterMerge Peer ClusterPowerState- | PartitionMerge Peer PartitionKey (PartitionPowerState i o s)+ | PartitionMerge Peer PartitionKey (PartitionPowerState e o s) {- |@@ -426,14 +426,14 @@ {- | Get the known peer data from the cluster. -}-getPeers :: SM i o s (Map Peer BSockAddr)+getPeers :: SM e o s (Map Peer BSockAddr) getPeers = SM $ C.getPeers . cluster <$> lift get {- | Gets a partition state. -}-getPartition :: (Default s, ApplyDelta i o s)+getPartition :: (Default s, Event e o s) => PartitionKey- -> SM i o s (PartitionPropState i o s)+ -> SM e o s (PartitionPropState e o s) getPartition key = SM $ do persistence <- ask NodeState {self, partitions, cluster} <- lift get@@ -449,10 +449,10 @@ Saves a partition state. This function automatically handles the cache for active propagations, as well as reindexing of partitions. -}-savePartition :: (Default s, ApplyDelta i o s, Indexable s)+savePartition :: (Default s, Event e o s, Indexable s) => PartitionKey- -> PartitionPropState i o s- -> SM i o s ()+ -> PartitionPropState e o s+ -> SM e o s () savePartition key partition = SM $ do persistence <- ask oldTags <- indexEntries . P.ask <$> unSM (getPartition key)