git.iko.soy/distributed-process
on master help sign in
Use new failure semantics in C.D.P.Node

Record as part of a connection whether to use ImplicitReconnect, and if so, delete if when we lose the connection.

commit24a027417473c43acbb1460cae31c4561a47941d authorEdsko de Vries <edsko@edsko.net> dateMon 27 Aug 2012, 14:51 (14 years ago) parentd0c14c7
6 files changed+94−67
distributed-process/src/Control/Distributed/Process/Internal/Messaging.hs+63−25 distributed-process/src/Control/Distributed/Process/Internal/Primitives.hs+1−1 distributed-process/src/Control/Distributed/Process/Internal/Types.hs+14−4 distributed-process/src/Control/Distributed/Process/Node.hs+6−25 distributed-process/tests/TestCH.hs+1 distributed-process/tests/TestClosure.hs+9−12
distributed-process/src/Control/Distributed/Process/Internal/Messaging.hs+63−25
@@ -1,19 +1,20 @@
11module Control.Distributed.Process.Internal.Messaging
2− ( -- * Message sending
3− ImplicitReconnect(WithImplicitReconnect, NoImplicitReconnect)
4− , sendPayload
2+ ( sendPayload
53 , sendBinary
64 , sendMessage
75 , disconnect
6+ , closeImplicitReconnections
7+ , impliesDeathOf
88 ) where
99
1010import Data.Accessor ((^.), (^=))
1111import Data.Binary (Binary, encode)
12+import qualified Data.Map as Map (partitionWithKey, elems)
1213import qualified Data.ByteString.Lazy as BSL (toChunks)
1314import qualified Data.ByteString as BSS (ByteString)
1415import Control.Distributed.Process.Internal.StrictMVar (withMVar, modifyMVar_)
1516import Control.Concurrent.Chan (writeChan)
16−import Control.Monad (unless, when)
17+import Control.Monad (unless)
1718import qualified Network.Transport as NT
1819 ( Connection
1920 , send
@@ -25,6 +26,7 @@ import qualified Network.Transport as NT
2526import Control.Distributed.Process.Internal.Types
2627 ( LocalNode(localState, localEndPoint, localCtrlChan)
2728 , Identifier
29+ , localConnections
2830 , localConnectionBetween
2931 , nodeAddress
3032 , nodeOf
@@ -33,6 +35,11 @@ import Control.Distributed.Process.Internal.Types
3335 , NCMsg(..)
3436 , ProcessSignal(Died)
3537 , DiedReason(DiedDisconnect)
38+ , ImplicitReconnect(WithImplicitReconnect)
39+ , NodeId
40+ , ProcessId(processNodeId)
41+ , SendPortId(sendPortProcessId)
42+ , Identifier(NodeIdentifier, ProcessIdentifier, SendPortIdentifier)
3643 )
3744import Control.Distributed.Process.Serializable (Serializable)
3845
@@ -40,9 +47,6 @@ import Control.Distributed.Process.Serializable (Serializable)
4047-- Message sending --
4148--------------------------------------------------------------------------------
4249
43−data ImplicitReconnect = WithImplicitReconnect | NoImplicitReconnect
44− deriving (Eq, Show)
45−
4650sendPayload :: LocalNode
4751 -> Identifier
4852 -> Identifier
@@ -50,7 +54,7 @@ sendPayload :: LocalNode
5054 -> [BSS.ByteString]
5155 -> IO ()
5256sendPayload node from to implicitReconnect payload = do
53− mConn <- connBetween node from to
57+ mConn <- connBetween node from to implicitReconnect
5458 didSend <- case mConn of
5559 Just conn -> do
5660 didSend <- NT.send conn payload
@@ -63,11 +67,6 @@ sendPayload node from to implicitReconnect payload = do
6367 { ctrlMsgSender = to
6468 , ctrlMsgSignal = Died to DiedDisconnect
6569 }
66− when (implicitReconnect == WithImplicitReconnect) $ do
67− disconnect node from to
68− -- Try once more
69− -- (TODO: not quite sure this is the Right Thing)
70− sendPayload node from to NoImplicitReconnect payload
7170
7271sendBinary :: Binary a
7372 => LocalNode
@@ -89,12 +88,16 @@ sendMessage :: Serializable a
8988sendMessage node from to implicitReconnect =
9089 sendPayload node from to implicitReconnect . messageToPayload . createMessage
9190
92−setupConnBetween :: LocalNode -> Identifier -> Identifier -> IO (Maybe NT.Connection)
93−setupConnBetween node from to = do
94− mConn <- NT.connect endPoint
95− (nodeAddress . nodeOf $ to)
96− NT.ReliableOrdered
97− NT.defaultConnectHints
91+setupConnBetween :: LocalNode
92+ -> Identifier
93+ -> Identifier
94+ -> ImplicitReconnect
95+ -> IO (Maybe NT.Connection)
96+setupConnBetween node from to implicitReconnect = do
97+ mConn <- NT.connect endPoint
98+ (nodeAddress . nodeOf $ to)
99+ NT.ReliableOrdered
100+ NT.defaultConnectHints
98101 case mConn of
99102 Right conn -> do
100103 didSend <- NT.send conn (BSL.toChunks . encode $ to)
@@ -103,7 +106,7 @@ setupConnBetween node from to = do
103106 return Nothing
104107 Right () -> do
105108 modifyMVar_ nodeState $ return .
106− (localConnectionBetween from to ^= Just conn)
109+ (localConnectionBetween from to ^= Just (conn, implicitReconnect))
107110 return $ Just conn
108111 Left _ ->
109112 return Nothing
@@ -111,12 +114,18 @@ setupConnBetween node from to = do
111114 endPoint = localEndPoint node
112115 nodeState = localState node
113116
114−connBetween :: LocalNode -> Identifier -> Identifier -> IO (Maybe NT.Connection)
115−connBetween node from to = do
117+connBetween :: LocalNode
118+ -> Identifier
119+ -> Identifier
120+ -> ImplicitReconnect
121+ -> IO (Maybe NT.Connection)
122+connBetween node from to implicitReconnect = do
116123 mConn <- withMVar nodeState $ return . (^. localConnectionBetween from to)
117124 case mConn of
118− Just conn -> return $ Just conn
119− Nothing -> setupConnBetween node from to
125+ Just (conn, _) ->
126+ return $ Just conn
127+ Nothing ->
128+ setupConnBetween node from to implicitReconnect
120129 where
121130 nodeState = localState node
122131
@@ -126,6 +135,35 @@ disconnect node from to =
126135 case st ^. localConnectionBetween from to of
127136 Nothing ->
128137 return st
129− Just conn -> do
138+ Just (conn, _) -> do
130139 NT.close conn
131140 return (localConnectionBetween from to ^= Nothing $ st)
141+
142+closeImplicitReconnections :: LocalNode -> Identifier -> IO ()
143+closeImplicitReconnections node to =
144+ modifyMVar_ (localState node) $ \st -> do
145+ let shouldClose (_, to') (_, WithImplicitReconnect) = to `impliesDeathOf` to'
146+ shouldClose _ _ = False
147+ let (affected, unaffected) = Map.partitionWithKey shouldClose (st ^. localConnections)
148+ mapM_ (NT.close . fst) (Map.elems affected)
149+ return (localConnections ^= unaffected $ st)
150+
151+-- | @a `impliesDeathOf` b@ is true if the death of @a@ (for instance, a node)
152+-- implies the death of @b@ (for instance, a process on that node)
153+impliesDeathOf :: Identifier
154+ -> Identifier
155+ -> Bool
156+NodeIdentifier nid `impliesDeathOf` NodeIdentifier nid' =
157+ nid' == nid
158+NodeIdentifier nid `impliesDeathOf` ProcessIdentifier pid =
159+ processNodeId pid == nid
160+NodeIdentifier nid `impliesDeathOf` SendPortIdentifier cid =
161+ processNodeId (sendPortProcessId cid) == nid
162+ProcessIdentifier pid `impliesDeathOf` ProcessIdentifier pid' =
163+ pid' == pid
164+ProcessIdentifier pid `impliesDeathOf` SendPortIdentifier cid =
165+ sendPortProcessId cid == pid
166+SendPortIdentifier cid `impliesDeathOf` SendPortIdentifier cid' =
167+ cid' == cid
168+_ `impliesDeathOf` _ =
169+ False
distributed-process/src/Control/Distributed/Process/Internal/Primitives.hs+1−1
@@ -121,11 +121,11 @@ import Control.Distributed.Process.Internal.Types
121121 , WhereIsReply(..)
122122 , createMessage
123123 , runLocalProcess
124+ , ImplicitReconnect(WithImplicitReconnect, NoImplicitReconnect)
124125 )
125126import Control.Distributed.Process.Internal.Messaging
126127 ( sendMessage
127128 , sendBinary
128− , ImplicitReconnect(WithImplicitReconnect, NoImplicitReconnect)
129129 , disconnect
130130 )
131131
distributed-process/src/Control/Distributed/Process/Internal/Types.hs+14−4
@@ -19,6 +19,7 @@ module Control.Distributed.Process.Internal.Types
1919 , LocalProcessState(..)
2020 , Process(..)
2121 , runLocalProcess
22+ , ImplicitReconnect(..)
2223 -- * Typed channels
2324 , LocalSendPortId
2425 , SendPortId(..)
@@ -181,12 +182,21 @@ data LocalNode = LocalNode
181182 , remoteTable :: RemoteTable
182183 }
183184
185+data ImplicitReconnect = WithImplicitReconnect | NoImplicitReconnect
186+ deriving (Eq, Show)
187+
184188-- | Local node state
185189data LocalNodeState = LocalNodeState
186− { _localProcesses :: !(Map LocalProcessId LocalProcess)
190+ { -- | Processes running on this node
191+ _localProcesses :: !(Map LocalProcessId LocalProcess)
192+ -- | Counter to assign PIDs
187193 , _localPidCounter :: !Int32
194+ -- | The 'unique' value used to create PIDs (so that processes on
195+ -- restarted nodes have new PIDs)
188196 , _localPidUnique :: !Int32
189− , _localConnections :: !(Map (Identifier, Identifier) NT.Connection)
197+ -- | Outgoing connections
198+ , _localConnections :: !(Map (Identifier, Identifier)
199+ (NT.Connection, ImplicitReconnect))
190200 }
191201
192202-- | Processes running on our local node
@@ -509,13 +519,13 @@ localPidCounter = accessor _localPidCounter (\ctr st -> st { _localPidCounter =
509519localPidUnique :: Accessor LocalNodeState Int32
510520localPidUnique = accessor _localPidUnique (\unq st -> st { _localPidUnique = unq })
511521
512−localConnections :: Accessor LocalNodeState (Map (Identifier, Identifier) NT.Connection)
522+localConnections :: Accessor LocalNodeState (Map (Identifier, Identifier) (NT.Connection, ImplicitReconnect))
513523localConnections = accessor _localConnections (\conns st -> st { _localConnections = conns })
514524
515525localProcessWithId :: LocalProcessId -> Accessor LocalNodeState (Maybe LocalProcess)
516526localProcessWithId lpid = localProcesses >>> DAC.mapMaybe lpid
517527
518−localConnectionBetween :: Identifier -> Identifier -> Accessor LocalNodeState (Maybe NT.Connection)
528+localConnectionBetween :: Identifier -> Identifier -> Accessor LocalNodeState (Maybe (NT.Connection, ImplicitReconnect))
519529localConnectionBetween from to = localConnections >>> DAC.mapMaybe (from, to)
520530
521531monitorCounter :: Accessor LocalProcessState Int32
distributed-process/src/Control/Distributed/Process/Node.hs+6−25
@@ -116,13 +116,15 @@ import Control.Distributed.Process.Internal.Types
116116 , createMessage
117117 , runLocalProcess
118118 , firstNonReservedProcessId
119+ , ImplicitReconnect(WithImplicitReconnect, NoImplicitReconnect)
119120 )
120121import Control.Distributed.Process.Serializable (Serializable)
121122import Control.Distributed.Process.Internal.Messaging
122123 ( sendBinary
123124 , sendMessage
124125 , sendPayload
125− , ImplicitReconnect(WithImplicitReconnect, NoImplicitReconnect)
126+ , closeImplicitReconnections
127+ , impliesDeathOf
126128 )
127129import Control.Distributed.Process.Internal.Primitives (expect, register, finally)
128130import qualified Control.Distributed.Process.Internal.Closure.BuiltIn as BuiltIn (remoteTable)
@@ -251,7 +253,7 @@ forkProcess node proc = modifyMVar (localState node) startProcess
251253 cleanupProcess pid st = do
252254 let pid' = ProcessIdentifier pid
253255 let (affected, unaffected) = Map.partitionWithKey (\(fr, _to) !_v -> impliesDeathOf pid' fr) (st ^. localConnections)
254− mapM_ NT.close (Map.elems affected)
256+ mapM_ (NT.close . fst) (Map.elems affected)
255257 return $ (localProcessWithId (processLocalId pid) ^= Nothing)
256258 . (localConnections ^= unaffected)
257259 $ st
@@ -364,12 +366,11 @@ handleIncomingMessages node = go initConnectionState
364366 , ctrlMsgSignal = Died nid DiedDisconnect
365367 }
366368 let notLost k = not (k `Set.member` (st ^. incomingFrom theirAddr))
369+ closeImplicitReconnections node nid
367370 go ( (incomingFrom theirAddr ^= Set.empty)
368371 . (incoming ^: Map.filterWithKey (const . notLost))
369372 $ st
370373 )
371− -- TODO: we should remove the outgoing connections for which
372− -- we have an implicit reconnect
373374 NT.ErrorEvent (NT.TransportError NT.EventEndPointFailed str) ->
374375 fail $ "Cloud Haskell fatal error: end point failed: " ++ str
375376 NT.ErrorEvent (NT.TransportError NT.EventTransportFailed str) ->
@@ -742,27 +743,7 @@ registryFor ident = registry >>> DAC.mapMaybe ident
742743splitNotif :: Identifier
743744 -> Map Identifier a
744745 -> (Map Identifier a, Map Identifier a)
745−splitNotif ident = Map.partitionWithKey (\k !_v -> impliesDeathOf ident k)
746−
747−-- | Does the death of one entity (node, project, channel) imply the death
748−-- of another?
749−impliesDeathOf :: Identifier -- ^ Who died
750− -> Identifier -- ^ Who's being watched
751− -> Bool -- ^ Does this death implies the death of the watchee?
752−NodeIdentifier nid `impliesDeathOf` NodeIdentifier nid' =
753− nid' == nid
754−NodeIdentifier nid `impliesDeathOf` ProcessIdentifier pid =
755− processNodeId pid == nid
756−NodeIdentifier nid `impliesDeathOf` SendPortIdentifier cid =
757− processNodeId (sendPortProcessId cid) == nid
758−ProcessIdentifier pid `impliesDeathOf` ProcessIdentifier pid' =
759− pid' == pid
760−ProcessIdentifier pid `impliesDeathOf` SendPortIdentifier cid =
761− sendPortProcessId cid == pid
762−SendPortIdentifier cid `impliesDeathOf` SendPortIdentifier cid' =
763− cid' == cid
764−_ `impliesDeathOf` _ =
765− False
746+splitNotif ident = Map.partitionWithKey (\k !_v -> ident `impliesDeathOf` k)
766747
767748--------------------------------------------------------------------------------
768749-- Strict evaluation of the state --
distributed-process/tests/TestCH.hs+1
@@ -621,6 +621,7 @@ testReconnect transport transportInternals = do
621621 liftIO $ do
622622 sock <- socketBetween transportInternals (nodeAddress nid1) (nodeAddress nid2)
623623 sClose sock
624+ threadDelay 10000
624625
625626 -- Should not arrive
626627 send them "message 2"
distributed-process/tests/TestClosure.hs+9−12
@@ -355,6 +355,12 @@ testTDict transport rtable = do
355355 liftIO $ putMVar done ()
356356 takeMVar done
357357
358+simulateNetworkFailure :: TransportInternals -> NodeId -> NodeId -> Process ()
359+simulateNetworkFailure transportInternals fr to = liftIO $ do
360+ threadDelay 10000
361+ sock <- socketBetween transportInternals (nodeAddress fr) (nodeAddress to)
362+ sClose sock
363+ threadDelay 10000
358364
359365testSpawnReconnect :: Transport -> RemoteTable -> TransportInternals -> IO ()
360366testSpawnReconnect transport rtable transportInternals = do
@@ -370,23 +376,14 @@ testSpawnReconnect transport rtable transportInternals = do
370376
371377 forkProcess node2 $ do
372378 _pid1 <- spawn nid1 ($(mkClosure 'signal) incr)
373−
374− -- Simulate network failure
375− liftIO $ do
376− sock <- socketBetween transportInternals (nodeAddress nid2) (nodeAddress nid1)
377− sClose sock
378−
379− -- Implicit reconnect: spawn happens; but since we also receive the
380− -- monitor signal, the process might not actually be started
379+ simulateNetworkFailure transportInternals nid2 nid1
381380 _pid2 <- spawn nid1 ($(mkClosure 'signal) incr)
382−
383− -- As does this one
384381 _pid3 <- spawn nid1 ($(mkClosure 'signal) incr)
385382
386− liftIO $ threadDelay 10000
383+ liftIO $ threadDelay 100000
387384
388385 count <- liftIO $ takeMVar iv
389− True <- return $ count == 2 || count == 3 -- Depending on whether we get the monitor notification first or not
386+ True <- return $ count == 3
390387
391388 liftIO $ putMVar done ()
392389