-
Notifications
You must be signed in to change notification settings - Fork 86
/
EstablishedPeers.hs
746 lines (688 loc) · 34.6 KB
/
EstablishedPeers.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Ouroboros.Network.PeerSelection.Governor.EstablishedPeers
( belowTarget
, aboveTarget
) where
import Data.Map.Strict (Map)
import Data.Map.Strict qualified as Map
import Data.Set (Set)
import Data.Set qualified as Set
import GHC.Stack (HasCallStack)
import Control.Applicative (Alternative)
import Control.Concurrent.JobPool (Job (..))
import Control.Exception (SomeException)
import Control.Monad.Class.MonadSTM
import Control.Monad.Class.MonadTime.SI
import System.Random (randomR)
import Ouroboros.Network.PeerSelection.Bootstrap (requiresBootstrapPeers)
import Ouroboros.Network.PeerSelection.Governor.Types
import Ouroboros.Network.PeerSelection.LedgerPeers.Type (IsBigLedgerPeer (..))
import Ouroboros.Network.PeerSelection.PeerAdvertise (PeerAdvertise (..))
import Ouroboros.Network.PeerSelection.PeerSharing (PeerSharing (..))
import Ouroboros.Network.PeerSelection.PublicRootPeers qualified as PublicRootPeers
import Ouroboros.Network.PeerSelection.State.EstablishedPeers qualified as EstablishedPeers
import Ouroboros.Network.PeerSelection.State.KnownPeers qualified as KnownPeers
import Ouroboros.Network.PeerSelection.State.LocalRootPeers (WarmValency (..))
import Ouroboros.Network.PeerSelection.State.LocalRootPeers qualified as LocalRootPeers
---------------------------------
-- Established peers below target
--
-- | If we are below the target of /warm peers/ we promote /cold peers/
-- according to 'policyPickColdPeersToPromote'.
--
-- There are two targets we are trying to hit here:
--
-- 1. a target for the overall number of established peers; and
-- 2. the target that all local root peers are established peers.
--
-- These two targets overlap: the conditions and the actions overlap since local
-- root peers are also known peers. Since they overlap, the order in which we
-- consider these targets is important. We consider the local peers target
-- /before/ the target for promoting other peers.
--
-- We will /always/ try to establish connections to the local root peers, even
-- if that would put us over target for the number of established peers. If we
-- do go over target then the action to demote will be triggered. The demote
-- action never picks local root peers.
--
belowTarget :: forall peeraddr peerconn m.
( Alternative (STM m)
, MonadSTM m
, Ord peeraddr
)
=> PeerSelectionActions peeraddr peerconn m
-> MkGuardedDecision peeraddr peerconn m
belowTarget = belowTargetBigLedgerPeers <> belowTargetLocal <> belowTargetOther
-- | For locally configured root peers we have the explicit target that comes from local
-- configuration.
--
belowTargetLocal :: forall peeraddr peerconn m.
(MonadSTM m, Ord peeraddr, HasCallStack)
=> PeerSelectionActions peeraddr peerconn m
-> MkGuardedDecision peeraddr peerconn m
belowTargetLocal actions
policy@PeerSelectionPolicy {
policyPickColdPeersToPromote
}
st@PeerSelectionState {
localRootPeers,
publicRootPeers,
knownPeers,
establishedPeers,
inProgressPromoteCold,
inProgressDemoteToCold
}
-- Are there any groups of local peers that are below target?
| not (null groupsBelowTarget)
-- We need this detailed check because it is not enough to check we are
-- below an aggregate target. We can be above target for some groups
-- and below for others. We need to take into account peers which are being
-- promoted to Warm, and peers which are being demoted to Cold.
-- Are there any groups where we can pick members to promote?
, let groupsAvailableToPromote =
[ (numMembersToPromote, membersAvailableToPromote)
| let availableToPromote =
localAvailableToConnect
Set.\\ localEstablishedPeers
Set.\\ localConnectInProgress
Set.\\ inProgressDemoteToCold
, not (Set.null availableToPromote)
, (WarmValency warmTarget, members, membersEstablished) <- groupsBelowTarget
, let membersAvailableToPromote = Set.intersection members availableToPromote
numMembersToPromote = warmTarget
- Set.size membersEstablished
- numLocalConnectInProgress
, not (Set.null membersAvailableToPromote)
, numMembersToPromote > 0
]
, not (null groupsAvailableToPromote)
= Guarded Nothing $ do
selectedToPromote <-
Set.unions <$> sequence
[ pickPeers st
policyPickColdPeersToPromote
membersAvailableToPromote
numMembersToPromote
| (numMembersToPromote,
membersAvailableToPromote) <- groupsAvailableToPromote ]
return $ \_now -> Decision {
decisionTrace = [TracePromoteColdLocalPeers
[ (target, Set.size membersEstablished)
| (target, _, membersEstablished) <- groupsBelowTarget ]
selectedToPromote],
decisionState = st {
inProgressPromoteCold = inProgressPromoteCold
<> selectedToPromote
},
decisionJobs = [ jobPromoteColdPeer actions policy peer IsNotBigLedgerPeer
| peer <- Set.toList selectedToPromote ]
}
-- If we could promote except that there are no peers currently available
-- then we return the next wakeup time (if any)
| not (null groupsBelowTarget)
, let potentialToPromote =
-- These are local peers that are cold but not ready.
localRootPeersSet
Set.\\ localEstablishedPeers
Set.\\ KnownPeers.availableToConnect knownPeers
, not (Set.null potentialToPromote)
= GuardedSkip (KnownPeers.minConnectTime knownPeers (`Set.notMember` bigLedgerPeersSet))
| otherwise
= GuardedSkip Nothing
where
groupsBelowTarget =
[ (warmValency, members, membersEstablished)
| (_, warmValency, members) <- LocalRootPeers.toGroupSets localRootPeers
, let membersEstablished = members `Set.intersection` EstablishedPeers.toSet establishedPeers
, Set.size membersEstablished < getWarmValency warmValency
]
localRootPeersSet = LocalRootPeers.keysSet localRootPeers
bigLedgerPeersSet = PublicRootPeers.getBigLedgerPeers publicRootPeers
localEstablishedPeers = EstablishedPeers.toSet establishedPeers
`Set.intersection` localRootPeersSet
localAvailableToConnect = KnownPeers.availableToConnect knownPeers
`Set.intersection` localRootPeersSet
localConnectInProgress = inProgressPromoteCold
`Set.intersection` localRootPeersSet
numLocalConnectInProgress = Set.size localConnectInProgress
belowTargetOther :: forall peeraddr peerconn m.
(MonadSTM m, Ord peeraddr, HasCallStack)
=> PeerSelectionActions peeraddr peerconn m
-> MkGuardedDecision peeraddr peerconn m
belowTargetOther actions
policy@PeerSelectionPolicy {
policyPickColdPeersToPromote
}
st@PeerSelectionState {
knownPeers,
publicRootPeers,
establishedPeers,
inProgressPromoteCold,
targets = PeerSelectionTargets {
targetNumberOfEstablishedPeers
}
}
-- Are we below the target for number of established peers?
| numEstablishedPeers + numConnectInProgress < targetNumberOfEstablishedPeers
-- Are there any cold peers we could possibly pick to connect to?
-- We can subtract the established ones because by definition they are
-- not cold and our invariant is that they are always in the connect set.
-- We can also subtract the in progress ones since they are also already
-- in the connect set and we cannot pick them again.
, numAvailableToConnect - numEstablishedPeers - numConnectInProgress > 0
= Guarded Nothing $ do
-- The availableToPromote here is non-empty due to the second guard.
-- The known peers map restricted to the connect set is the same size as
-- the connect set (because it is a subset). The establishedPeers is a
-- subset of the connect set and we also know that there is no overlap
-- between inProgressPromoteCold and establishedPeers. QED.
--
-- The numPeersToPromote is positive based on the first guard.
--
let availableToPromote :: Set peeraddr
availableToPromote = availableToConnect
Set.\\ EstablishedPeers.toSet establishedPeers
Set.\\ inProgressPromoteCold
numPeersToPromote = targetNumberOfEstablishedPeers
- numEstablishedPeers
- numConnectInProgress
selectedToPromote <- pickPeers st
policyPickColdPeersToPromote
availableToPromote
numPeersToPromote
return $ \_now -> Decision {
decisionTrace = [TracePromoteColdPeers
targetNumberOfEstablishedPeers
numEstablishedPeers
selectedToPromote],
decisionState = st {
inProgressPromoteCold = inProgressPromoteCold
<> selectedToPromote
},
decisionJobs = [ jobPromoteColdPeer actions policy peer IsNotBigLedgerPeer
| peer <- Set.toList selectedToPromote ]
}
-- If we could connect except that there are no peers currently available
-- then we return the next wakeup time (if any)
| numEstablishedPeers + numConnectInProgress < targetNumberOfEstablishedPeers
= GuardedSkip (KnownPeers.minConnectTime knownPeers (`Set.notMember` bigLedgerPeersSet))
| otherwise
= GuardedSkip Nothing
where
PeerSelectionCounters {
numberOfEstablishedPeers = numEstablishedPeers,
numberOfColdPeersPromotions = numConnectInProgress
}
=
peerSelectionStateToCounters st
bigLedgerPeersSet = PublicRootPeers.getBigLedgerPeers publicRootPeers
availableToConnect = KnownPeers.availableToConnect knownPeers
Set.\\ bigLedgerPeersSet
numAvailableToConnect = Set.size availableToConnect
-- |
--
-- It should be noted if the node is in bootstrap mode (i.e. in a sensitive
-- state) then this monitoring action will be disabled.
--
belowTargetBigLedgerPeers :: forall peeraddr peerconn m.
(MonadSTM m, Ord peeraddr, HasCallStack)
=> PeerSelectionActions peeraddr peerconn m
-> MkGuardedDecision peeraddr peerconn m
belowTargetBigLedgerPeers actions
policy@PeerSelectionPolicy {
policyPickColdPeersToPromote
}
st@PeerSelectionState {
knownPeers,
publicRootPeers,
establishedPeers,
inProgressPromoteCold,
targets = PeerSelectionTargets {
targetNumberOfEstablishedBigLedgerPeers
},
ledgerStateJudgement,
bootstrapPeersFlag
}
-- Are we below the target for number of established peers?
| numEstablishedPeers + numConnectInProgress
< targetNumberOfEstablishedBigLedgerPeers
-- Are there any cold peers we could possibly pick to connect to?
-- We can subtract the established ones because by definition they are
-- not cold and our invariant is that they are always in the connect set.
-- We can also subtract the in progress ones since they are also already
-- in the connect set and we cannot pick them again.
, numAvailableToConnect - numEstablishedPeers - numConnectInProgress > 0
-- Are we in insensitive state, i.e. using bootstrap peers?
, not (requiresBootstrapPeers bootstrapPeersFlag ledgerStateJudgement)
= Guarded Nothing $ do
-- The availableToPromote here is non-empty due to the second guard.
-- The known peers map restricted to the connect set is the same size as
-- the connect set (because it is a subset). The establishedPeers is a
-- subset of the connect set and we also know that there is no overlap
-- between inProgressPromoteCold and establishedPeers. QED.
--
-- The numPeersToPromote is positive based on the first guard.
--
let availableToPromote :: Set peeraddr
availableToPromote = availableToConnect
Set.\\ EstablishedPeers.toSet establishedPeers
Set.\\ inProgressPromoteCold
numPeersToPromote = targetNumberOfEstablishedBigLedgerPeers
- numEstablishedPeers
- numConnectInProgress
selectedToPromote <- pickPeers st
policyPickColdPeersToPromote
availableToPromote
numPeersToPromote
return $ \_now -> Decision {
decisionTrace = [TracePromoteColdBigLedgerPeers
targetNumberOfEstablishedBigLedgerPeers
numEstablishedPeers
selectedToPromote],
decisionState = st {
inProgressPromoteCold = inProgressPromoteCold
<> selectedToPromote
},
decisionJobs = [ jobPromoteColdPeer actions policy peer IsBigLedgerPeer
| peer <- Set.toList selectedToPromote ]
}
-- If we could connect except that there are no peers currently available
-- then we return the next wakeup time (if any)
| numEstablishedPeers + numConnectInProgress
< targetNumberOfEstablishedBigLedgerPeers
= GuardedSkip (KnownPeers.minConnectTime knownPeers (`Set.member` bigLedgerPeersSet))
| otherwise
= GuardedSkip Nothing
where
PeerSelectionCounters {
numberOfEstablishedBigLedgerPeers = numEstablishedPeers,
numberOfColdBigLedgerPeersPromotions = numConnectInProgress
}
=
peerSelectionStateToCounters st
bigLedgerPeersSet = PublicRootPeers.getBigLedgerPeers publicRootPeers
availableToConnect = KnownPeers.availableToConnect knownPeers
`Set.intersection`
bigLedgerPeersSet
numAvailableToConnect= Set.size availableToConnect
-- | Must be larger than '2' since we add a random value drawn from '(-2, 2)`.
--
baseColdPeerRetryDiffTime :: Int
baseColdPeerRetryDiffTime = 5
maxColdPeerRetryBackoff :: Int
maxColdPeerRetryBackoff = 5
jobPromoteColdPeer :: forall peeraddr peerconn m.
(Monad m, Ord peeraddr)
=> PeerSelectionActions peeraddr peerconn m
-> PeerSelectionPolicy peeraddr m
-> peeraddr
-> IsBigLedgerPeer
-> Job () m (Completion m peeraddr peerconn)
jobPromoteColdPeer PeerSelectionActions {
peerStateActions = PeerStateActions {establishPeerConnection},
peerConnToPeerSharing
}
PeerSelectionPolicy { policyPeerShareActivationDelay }
peeraddr isBigLedgerPeer =
Job job handler () "promoteColdPeer"
where
handler :: SomeException -> m (Completion m peeraddr peerconn)
handler e = return $
Completion $ \st@PeerSelectionState {
publicRootPeers,
establishedPeers,
fuzzRng,
targets = PeerSelectionTargets {
targetNumberOfEstablishedPeers,
targetNumberOfEstablishedBigLedgerPeers
}
}
now ->
let (failCount, knownPeers') = KnownPeers.incrementFailCount
peeraddr
(knownPeers st)
(fuzz, fuzzRng') = randomR (-2, 2 :: Double) fuzzRng
-- exponential backoff: 5s, 10s, 20s, 40s, 80s, 160s.
delay :: DiffTime
delay = realToFrac fuzz
+ fromIntegral
( baseColdPeerRetryDiffTime
* 2 ^ (pred failCount `min` maxColdPeerRetryBackoff)
)
bigLedgerPeersSet = PublicRootPeers.getBigLedgerPeers publicRootPeers
in
Decision {
decisionTrace = if peeraddr `Set.member` bigLedgerPeersSet
then [TracePromoteColdBigLedgerPeerFailed
targetNumberOfEstablishedBigLedgerPeers
(Set.size $ EstablishedPeers.toSet establishedPeers
`Set.intersection`
bigLedgerPeersSet)
peeraddr delay e]
else [TracePromoteColdFailed
targetNumberOfEstablishedPeers
(EstablishedPeers.size establishedPeers)
peeraddr delay e],
decisionState = st {
knownPeers = KnownPeers.setConnectTimes
(Map.singleton
peeraddr
(delay `addTime` now))
knownPeers',
inProgressPromoteCold = Set.delete peeraddr
(inProgressPromoteCold st),
fuzzRng = fuzzRng'
},
decisionJobs = []
}
job :: m (Completion m peeraddr peerconn)
job = do
--TODO: decide if we should do timeouts here or if we should make that
-- the responsibility of establishPeerConnection
peerconn <- establishPeerConnection isBigLedgerPeer peeraddr
let !peerSharing = peerConnToPeerSharing peerconn
return $ Completion $ \st@PeerSelectionState {
publicRootPeers,
establishedPeers,
knownPeers,
targets = PeerSelectionTargets {
targetNumberOfEstablishedPeers,
targetNumberOfEstablishedBigLedgerPeers
}
}
now ->
let psTime = case peerSharing of
PeerSharingEnabled -> Just (addTime policyPeerShareActivationDelay now)
PeerSharingDisabled -> Nothing
establishedPeers' = EstablishedPeers.insert peeraddr peerconn psTime establishedPeers
advertise = case peerSharing of
PeerSharingEnabled -> DoAdvertisePeer
PeerSharingDisabled -> DoNotAdvertisePeer
-- Update PeerSharing value in KnownPeers
knownPeers' = KnownPeers.alter
(\x -> case x of
Nothing ->
KnownPeers.alterKnownPeerInfo
(Just peerSharing, Just advertise)
x
Just _ ->
KnownPeers.alterKnownPeerInfo
(Just peerSharing, Nothing)
x
)
(Set.singleton peeraddr)
$ KnownPeers.setSuccessfulConnectionFlag peeraddr
$ KnownPeers.clearTepidFlag peeraddr $
KnownPeers.resetFailCount
peeraddr
knownPeers
bigLedgerPeersSet = PublicRootPeers.getBigLedgerPeers publicRootPeers
in Decision {
decisionTrace = if peeraddr `Set.member` bigLedgerPeersSet
then [TracePromoteColdBigLedgerPeerDone
targetNumberOfEstablishedBigLedgerPeers
(Set.size $ EstablishedPeers.toSet establishedPeers'
`Set.intersection`
bigLedgerPeersSet)
peeraddr]
else [TracePromoteColdDone
targetNumberOfEstablishedPeers
(Set.size $ EstablishedPeers.toSet establishedPeers'
Set.\\ bigLedgerPeersSet)
peeraddr],
decisionState = st {
establishedPeers = establishedPeers',
inProgressPromoteCold = Set.delete peeraddr
(inProgressPromoteCold st),
knownPeers = knownPeers'
},
decisionJobs = []
}
---------------------------------
-- Established peers above target
--
--
-- | If we are above the target of /established peers/ we demote some of the
-- /warm peers/ to the cold state, according to 'policyPickWarmPeersToDemote'.
--
aboveTarget :: forall peeraddr peerconn m.
(Alternative (STM m), MonadSTM m, Ord peeraddr)
=> PeerSelectionActions peeraddr peerconn m
-> MkGuardedDecision peeraddr peerconn m
aboveTarget = aboveTargetBigLedgerPeers <> aboveTargetOther
aboveTargetOther :: forall peeraddr peerconn m.
(MonadSTM m, Ord peeraddr, HasCallStack)
=> PeerSelectionActions peeraddr peerconn m
-> MkGuardedDecision peeraddr peerconn m
aboveTargetOther actions
PeerSelectionPolicy {
policyPickWarmPeersToDemote
}
st@PeerSelectionState {
localRootPeers,
publicRootPeers,
establishedPeers,
activePeers,
inProgressDemoteWarm,
inProgressPromoteWarm,
inProgressDemoteToCold,
targets = PeerSelectionTargets {
targetNumberOfEstablishedPeers
}
}
-- Are we above the target for number of established peers?
-- Or more precisely, how many established peers could we demote?
-- We only want to pick established peers that are not active, since for
-- active one we need to demote them first.
| let bigLedgerPeersSet = PublicRootPeers.getBigLedgerPeers publicRootPeers
numActivePeers, numPeersToDemote :: Int
PeerSelectionCounters {
numberOfEstablishedPeers = numEstablishedPeers,
numberOfActivePeers = numActivePeers
}
=
peerSelectionStateToCounters st
numLocalWarmPeers = Set.size localWarmPeers
localWarmPeers = LocalRootPeers.keysSet localRootPeers
`Set.intersection` EstablishedPeers.toSet establishedPeers
Set.\\ activePeers
Set.\\ bigLedgerPeersSet
-- One constraint on how many to demote is the difference in the
-- number we have now vs the target. The other constraint is that
-- we pick established peers that are not also active. These
-- constraints combine by taking the minimum. We must also subtract
-- the number we're demoting so we don't repeat the same work. And
-- cannot demote ones we're in the process of promoting.
numPeersToDemote = min (numEstablishedPeers
- targetNumberOfEstablishedPeers)
(numEstablishedPeers
- numLocalWarmPeers
- numActivePeers)
- Set.size (inProgressDemoteWarm Set.\\ bigLedgerPeersSet)
- Set.size (inProgressPromoteWarm Set.\\ bigLedgerPeersSet)
availableToDemote :: Set peeraddr
availableToDemote = EstablishedPeers.toSet establishedPeers
Set.\\ activePeers
Set.\\ LocalRootPeers.keysSet localRootPeers
Set.\\ bigLedgerPeersSet
Set.\\ inProgressDemoteWarm
Set.\\ inProgressPromoteWarm
Set.\\ inProgressDemoteToCold
, numPeersToDemote > 0
, not (Set.null availableToDemote)
= Guarded Nothing $ do
selectedToDemote <- pickPeers st
policyPickWarmPeersToDemote
availableToDemote
numPeersToDemote
let selectedToDemote' :: Map peeraddr peerconn
selectedToDemote' = EstablishedPeers.toMap establishedPeers
`Map.restrictKeys` selectedToDemote
return $ \_now -> Decision {
decisionTrace = [TraceDemoteWarmPeers
targetNumberOfEstablishedPeers
numEstablishedPeers
selectedToDemote],
decisionState = st {
inProgressDemoteWarm = inProgressDemoteWarm
<> selectedToDemote
},
decisionJobs = [ jobDemoteEstablishedPeer actions peeraddr peerconn
| (peeraddr, peerconn) <- Map.assocs selectedToDemote' ]
}
| otherwise
= GuardedSkip Nothing
aboveTargetBigLedgerPeers :: forall peeraddr peerconn m.
(MonadSTM m, Ord peeraddr, HasCallStack)
=> PeerSelectionActions peeraddr peerconn m
-> MkGuardedDecision peeraddr peerconn m
aboveTargetBigLedgerPeers actions
PeerSelectionPolicy {
policyPickWarmPeersToDemote
}
st@PeerSelectionState {
publicRootPeers,
establishedPeers,
activePeers,
inProgressDemoteWarm,
inProgressPromoteWarm,
inProgressDemoteToCold,
targets = PeerSelectionTargets {
targetNumberOfEstablishedBigLedgerPeers
}
}
-- Are we above the target for number of established peers?
-- Or more precisely, how many established peers could we demote?
-- We only want to pick established peers that are not active, since for
-- active one we need to demote them first.
| let bigLedgerPeersSet = PublicRootPeers.getBigLedgerPeers publicRootPeers
PeerSelectionCounters {
numberOfEstablishedBigLedgerPeers = numEstablishedBigLedgerPeers,
numberOfActiveBigLedgerPeers = numActiveBigLedgerPeers
}
=
peerSelectionStateToCounters st
-- We want to demote big ledger peers towards the target but we avoid to
-- pick active peer. The `min` is taken so that `pickPeers` is given
-- consistent number of peers with the set of peers available to demote,
-- i.e. `availableToDemote`.
numBigLedgerPeersToDemote = min ( numEstablishedBigLedgerPeers
- targetNumberOfEstablishedBigLedgerPeers)
( numEstablishedBigLedgerPeers
- numActiveBigLedgerPeers)
- Set.size inProgressDemoteWarm
- Set.size inProgressPromoteWarm
availableToDemote :: Set peeraddr
availableToDemote = EstablishedPeers.toSet establishedPeers
`Set.intersection` bigLedgerPeersSet
Set.\\ activePeers
Set.\\ inProgressDemoteWarm
Set.\\ inProgressPromoteWarm
Set.\\ inProgressDemoteToCold
, numBigLedgerPeersToDemote > 0
, not (Set.null availableToDemote)
= Guarded Nothing $ do
selectedToDemote <- pickPeers st
policyPickWarmPeersToDemote
availableToDemote
numBigLedgerPeersToDemote
let selectedToDemote' :: Map peeraddr peerconn
selectedToDemote' = EstablishedPeers.toMap establishedPeers
`Map.restrictKeys` selectedToDemote
return $ \_now -> Decision {
decisionTrace = [TraceDemoteWarmBigLedgerPeers
targetNumberOfEstablishedBigLedgerPeers
numEstablishedBigLedgerPeers
selectedToDemote],
decisionState = st {
inProgressDemoteWarm = inProgressDemoteWarm
<> selectedToDemote
},
decisionJobs = [ jobDemoteEstablishedPeer actions peeraddr peerconn
| (!peeraddr, !peerconn) <- Map.assocs selectedToDemote' ]
}
| otherwise
= GuardedSkip Nothing
jobDemoteEstablishedPeer :: forall peeraddr peerconn m.
(Monad m, Ord peeraddr)
=> PeerSelectionActions peeraddr peerconn m
-> peeraddr
-> peerconn
-> Job () m (Completion m peeraddr peerconn)
jobDemoteEstablishedPeer PeerSelectionActions{peerStateActions = PeerStateActions {closePeerConnection}}
peeraddr peerconn =
Job job handler () "demoteEstablishedPeer"
where
handler :: SomeException -> m (Completion m peeraddr peerconn)
handler e = return $
-- It's quite bad if closing fails. The peer is cooling so
-- we can't remove it from the set of established peers.
--
Completion $ \st@PeerSelectionState {
publicRootPeers,
establishedPeers,
inProgressDemoteWarm,
targets = PeerSelectionTargets {
targetNumberOfEstablishedPeers,
targetNumberOfEstablishedBigLedgerPeers
}
}
_ ->
let inProgressDemoteWarm' = Set.delete peeraddr inProgressDemoteWarm
bigLedgerPeersSet = PublicRootPeers.getBigLedgerPeers publicRootPeers
in
Decision {
decisionTrace = if peeraddr `Set.member` bigLedgerPeersSet
then [TraceDemoteWarmBigLedgerPeerFailed
targetNumberOfEstablishedBigLedgerPeers
(Set.size $ EstablishedPeers.toSet establishedPeers
`Set.intersection`
bigLedgerPeersSet)
peeraddr e]
else [TraceDemoteWarmFailed
targetNumberOfEstablishedPeers
(Set.size $ EstablishedPeers.toSet establishedPeers
Set.\\ bigLedgerPeersSet)
peeraddr e],
decisionState = st {
inProgressDemoteWarm = inProgressDemoteWarm'
},
decisionJobs = []
}
job :: m (Completion m peeraddr peerconn)
job = do
closePeerConnection peerconn
return $ Completion $ \st@PeerSelectionState {
publicRootPeers,
establishedPeers,
targets = PeerSelectionTargets {
targetNumberOfEstablishedPeers
}
}
_now ->
let establishedPeers' = EstablishedPeers.delete peeraddr
establishedPeers
bigLedgerPeersSet = PublicRootPeers.getBigLedgerPeers publicRootPeers
in Decision {
decisionTrace = if peeraddr `Set.member` bigLedgerPeersSet
then [TraceDemoteWarmBigLedgerPeerDone
targetNumberOfEstablishedPeers
(Set.size $ EstablishedPeers.toSet establishedPeers'
`Set.intersection`
bigLedgerPeersSet)
peeraddr]
else [TraceDemoteWarmDone
targetNumberOfEstablishedPeers
(Set.size $ EstablishedPeers.toSet establishedPeers'
Set.\\ bigLedgerPeersSet)
peeraddr],
decisionState = st {
establishedPeers = establishedPeers',
inProgressDemoteWarm = Set.delete peeraddr
(inProgressDemoteWarm st)
},
decisionJobs = []
}