Skip to content

Commit

Permalink
p2p-governor: introduce EstablishedPeers
Browse files Browse the repository at this point in the history
EstablisehdPeers keepsp track of established peer connection and status.
It is using the same logic as KnownPeers to track peers which are not
ready to promote to hot state, though this patch is not using this
mechanism, it just replaces the original logic.
  • Loading branch information
coot committed Oct 22, 2020
1 parent 7a6ab82 commit 24af325
Show file tree
Hide file tree
Showing 10 changed files with 294 additions and 67 deletions.
1 change: 1 addition & 0 deletions ouroboros-network/ouroboros-network.cabal
Expand Up @@ -65,6 +65,7 @@ library
Ouroboros.Network.Point
Ouroboros.Network.PeerSelection.Types
Ouroboros.Network.PeerSelection.KnownPeers
Ouroboros.Network.PeerSelection.EstablishedPeers
Ouroboros.Network.PeerSelection.PeerStateActions
Ouroboros.Network.PeerSelection.RootPeersDNS
Ouroboros.Network.PeerSelection.Governor
Expand Down
@@ -0,0 +1,231 @@
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE NamedFieldPuns #-}

{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}

module Ouroboros.Network.PeerSelection.EstablishedPeers
( EstablishedPeers
, establishedReady
, establishedStatus
, empty
, toMap

, size

, member

, insert
, delete
, deletePeers

, updateStatus
, updateStatuses

, setCurrentTime
, minActivateTime
, setActivateTime

, invariant
) where

import Prelude

import Data.Foldable (foldl')
import qualified Data.Map.Strict as Map
import Data.Map.Strict (Map)
import Data.OrdPSQ (OrdPSQ)
import qualified Data.OrdPSQ as PSQ
import qualified Data.Set as Set
import Data.Set (Set)

import Control.Monad.Class.MonadTime
import Control.Exception (assert)

import Ouroboros.Network.PeerSelection.Types


data EstablishedPeers peeraddr peerconn = EstablishedPeers {
-- | Peers which are either ready do become active or are active.
--
establishedReady :: !(Map peeraddr peerconn),

-- | 'PeerStatus' of all established peers.
establishedStatus :: !(Map peeraddr PeerStatus),

-- | Peers which are not ready to become active.
nextActivateTimes :: !(OrdPSQ peeraddr Time peerconn)
}
deriving (Show, Functor)


empty :: EstablishedPeers peeraddr perconn
empty = EstablishedPeers Map.empty Map.empty PSQ.empty


invariant :: Ord peeraddr
=> EstablishedPeers peeraddr peerconn
-> Bool
invariant EstablishedPeers { establishedReady,
establishedStatus,
nextActivateTimes } =
-- established connections and nextActivateTimes do not intersect
Set.null
(Set.intersection
(Map.keysSet establishedReady)
(Set.fromList (PSQ.keys nextActivateTimes)))

-- established connection + nextActivate times has the same keys as
-- established status
&& Map.keysSet establishedReady
<> (Set.fromList (PSQ.keys nextActivateTimes))
== Map.keysSet establishedStatus

-- there are ony warm peers in 'nextActiveTimes'
&& all (== PeerWarm)
(Map.filterWithKey
(\peeraddr _ -> PSQ.member peeraddr nextActivateTimes)
establishedStatus)


-- | Map of all established connections.
--
-- Complexity: /O(n*log n)/
--
toMap :: Ord peeraddr
=> EstablishedPeers peeraddr peerconn
-> Map peeraddr peerconn
toMap EstablishedPeers { establishedReady, nextActivateTimes } =
establishedReady
<> Map.fromList [ (peeraddr, peerconn)
| (peeraddr, _, peerconn) <- PSQ.toList nextActivateTimes ]


size :: EstablishedPeers peeraddr peerconn -> Int
size = Map.size . establishedStatus


member :: Ord peeraddr => peeraddr -> EstablishedPeers peeraddr peerconn -> Bool
member peeraddr = Map.member peeraddr . establishedStatus


-- | Insert a peer into 'EstablishedPeers'.
--
insert :: Ord peeraddr
=> peeraddr
-> peerconn
-> EstablishedPeers peeraddr peerconn
-> EstablishedPeers peeraddr peerconn
insert peeraddr peerconn ep@EstablishedPeers { establishedReady, establishedStatus } =
ep { establishedReady = Map.insert peeraddr peerconn establishedReady,
establishedStatus = Map.insert peeraddr PeerWarm establishedStatus }

updateStatus :: Ord peeraddr
=> peeraddr
-> PeerStatus
-- ^ keys must be a subset of keys of 'establishedStatus' map
-> EstablishedPeers peeraddr peerconn
-> EstablishedPeers peeraddr peerconn
updateStatus peeraddr peerStatus ep@EstablishedPeers { establishedStatus } =
assert (Map.member peeraddr establishedStatus) $
ep { establishedStatus = Map.insert peeraddr peerStatus establishedStatus }

-- | Update 'establishedStatus' map.
--
updateStatuses :: Ord peeraddr
=> Map peeraddr PeerStatus
-- ^ keys must be a subset of keys of 'establishedStatus' map
-> EstablishedPeers peeraddr peerconn
-> EstablishedPeers peeraddr peerconn
updateStatuses newStatuses ep@EstablishedPeers { establishedStatus } =
assert (Map.isSubmapOfBy (\_ _ -> True) newStatuses establishedStatus) $
ep { establishedStatus = newStatuses <> establishedStatus }


delete :: Ord peeraddr
=> peeraddr
-> EstablishedPeers peeraddr peerconn
-> EstablishedPeers peeraddr peerconn
delete peeraddr es@EstablishedPeers { establishedReady,
establishedStatus,
nextActivateTimes } =
es { establishedReady = Map.delete peeraddr establishedReady,
establishedStatus = Map.delete peeraddr establishedStatus,
nextActivateTimes = PSQ.delete peeraddr nextActivateTimes }



-- | Bulk delete of peers from 'EstablishedPeers.
--
deletePeers :: Ord peeraddr
=> Set peeraddr
-> EstablishedPeers peeraddr peerconn
-> EstablishedPeers peeraddr peerconn
deletePeers peeraddrs es@EstablishedPeers { establishedReady,
establishedStatus,
nextActivateTimes } =
es { establishedReady = foldl' (flip Map.delete) establishedReady peeraddrs,
establishedStatus = foldl' (flip Map.delete) establishedStatus peeraddrs,
nextActivateTimes = foldl' (flip PSQ.delete) nextActivateTimes peeraddrs }


--
-- Time managment
--

setCurrentTime :: Ord peeraddr
=> Time
-> EstablishedPeers peeraddr peerconn
-> EstablishedPeers peeraddr peerconn
setCurrentTime now ep@EstablishedPeers { establishedReady,
nextActivateTimes } =
let ep' = ep { establishedReady =
Map.fromList [ (peeraddr, peerconn)
| (peeraddr, _, peerconn) <- nowAvailableToActivate ]
<> establishedReady,
nextActivateTimes = nextActivateTimes' }
in assert (invariant ep') ep'
where
(nowAvailableToActivate, nextActivateTimes') =
PSQ.atMostView now nextActivateTimes


minActivateTime :: Ord peeraddr
=> EstablishedPeers peeraddr peerconn
-> Maybe Time
minActivateTime EstablishedPeers {
establishedReady,
nextActivateTimes
}
| Map.null establishedReady
, Just (_k, t, _, _psq) <- PSQ.minView nextActivateTimes
= Just t

| otherwise
= Nothing


setActivateTime :: Ord peeraddr
=> Set peeraddr
-> Time
-> EstablishedPeers peeraddr peerconn
-> EstablishedPeers peeraddr peerconn
setActivateTime peeraddrs _time ep | Set.null peeraddrs = ep
setActivateTime peeraddrs time ep =
let ep' = foldl'
(\peers@EstablishedPeers { establishedReady = ready,
nextActivateTimes = times }
peeraddr ->

-- 'alterF' allows to delete and return the value
case Map.alterF (\x -> (x, Nothing)) peeraddr ready of
(Just peerconn, ready') ->
peers { establishedReady = ready',
nextActivateTimes = PSQ.insert peeraddr time peerconn times }

(Nothing, _ready') ->
error "EstablishedPeers.setActivateTime: invariant violation")
ep
peeraddrs
in assert (all (not . (`Map.member` establishedReady ep')) peeraddrs)
. assert (invariant ep')
$ ep'
Expand Up @@ -473,7 +473,7 @@ peerSelectionGovernorLoop tracer debugTracer actions policy jobPool =
loop !st = assertPeerSelectionState st $ do
blockedAt <- getMonotonicTime
let knownPeers' = KnownPeers.setCurrentTime blockedAt (knownPeers st)
st' = st { knownPeers = knownPeers' }
st' = st { knownPeers = knownPeers' }

timedDecision <- evalGuardedDecisions blockedAt st'

Expand Down
Expand Up @@ -17,6 +17,7 @@ import Control.Concurrent.JobPool (Job(..))
import Control.Exception (SomeException, assert)

import Ouroboros.Network.PeerSelection.Types
import qualified Ouroboros.Network.PeerSelection.EstablishedPeers as EstablishedPeers
import Ouroboros.Network.PeerSelection.KnownPeers (KnownPeerInfo(..))
import qualified Ouroboros.Network.PeerSelection.KnownPeers as KnownPeers
import Ouroboros.Network.PeerSelection.Governor.Types
Expand Down Expand Up @@ -59,7 +60,7 @@ belowTarget actions
-- The numPeersToPromote is positive due to the first guard.
let availableToPromote :: Map peeraddr KnownPeerInfo
availableToPromote = KnownPeers.toMap knownPeers
`Map.intersection` establishedPeers
`Map.intersection` EstablishedPeers.establishedReady establishedPeers
`Map.withoutKeys` activePeers
`Map.withoutKeys` inProgressPromoteWarm
`Map.withoutKeys` inProgressDemoteWarm
Expand All @@ -71,7 +72,7 @@ belowTarget actions
availableToPromote
numPeersToPromote
let selectedToPromote' :: Map peeraddr peerconn
selectedToPromote' = establishedPeers
selectedToPromote' = EstablishedPeers.establishedReady establishedPeers
`Map.restrictKeys` selectedToPromote
return $ \_now -> Decision {
decisionTrace = TracePromoteWarmPeers
Expand All @@ -90,7 +91,7 @@ belowTarget actions
= GuardedSkip Nothing
where
numEstablishedPeers, numActivePeers, numPromoteInProgress :: Int
numEstablishedPeers = Map.size establishedPeers
numEstablishedPeers = EstablishedPeers.size establishedPeers
numActivePeers = Set.size activePeers
numPromoteInProgress = Set.size inProgressPromoteWarm
numDemoteInProgress = Set.size inProgressDemoteWarm
Expand Down Expand Up @@ -125,14 +126,15 @@ jobPromoteWarmPeer PeerSelectionActions{peerStateActions = PeerStateActions {act
-- the responsibility of activatePeerConnection
activatePeerConnection peerconn
return $ Completion $ \st _now ->
assert (peeraddr `Map.member` establishedPeers st)
assert (peeraddr `EstablishedPeers.member` establishedPeers st)
Decision {
decisionTrace = TracePromoteWarmDone peeraddr,
decisionState = st {
activePeers = Set.insert peeraddr
(activePeers st),
establishedStatus = Map.insert peeraddr PeerHot
(establishedStatus st),
establishedPeers = EstablishedPeers.updateStatus
peeraddr PeerHot
(establishedPeers st),
inProgressPromoteWarm = Set.delete peeraddr
(inProgressPromoteWarm st)
},
Expand Down Expand Up @@ -186,7 +188,7 @@ aboveTarget actions
availableToDemote
numPeersToDemote
let selectedToDemote' :: Map peeraddr peerconn
selectedToDemote' = establishedPeers
selectedToDemote' = EstablishedPeers.establishedReady establishedPeers
`Map.restrictKeys` selectedToDemote

return $ \_now -> Decision {
Expand Down Expand Up @@ -235,14 +237,14 @@ jobDemoteActivePeer PeerSelectionActions{peerStateActions = PeerStateActions {de
job = do
deactivatePeerConnection peerconn
return $ Completion $ \st _now ->
assert (peeraddr `Map.member` establishedPeers st)
assert (peeraddr `EstablishedPeers.member` establishedPeers st)
Decision {
decisionTrace = TraceDemoteHotDone peeraddr,
decisionState = st {
activePeers = Set.delete peeraddr
(activePeers st),
establishedStatus = Map.insert peeraddr PeerWarm
(establishedStatus st),
establishedPeers = EstablishedPeers.updateStatus peeraddr PeerWarm
(establishedPeers st),
inProgressDemoteHot = Set.delete peeraddr
(inProgressDemoteHot st)
},
Expand Down

0 comments on commit 24af325

Please sign in to comment.