|
11 | 11 | -- specific to the bulk sync mode. This logic reuses parts of the logic for the |
12 | 12 | -- deadline mode, but it is inherently different. |
13 | 13 | -- |
14 | | --- Natural language specification |
15 | | --- ------------------------------ |
16 | | --- |
17 | 14 | -- Definitions: |
18 | 15 | -- |
19 | 16 | -- - Let @inflight :: peer -> Set blk@ be the outstanding blocks, those that |
|
53 | 50 | -- 2. Select @thePeer :: peer@. If @inflight(currentPeer)@ is not empty, then |
54 | 51 | -- this is @currentPeer@. Otherwise: |
55 | 52 | -- |
56 | | --- - Let @grossRequest@ be the oldest blocks on @theCandidate@ that have not |
57 | | --- already been downloaded and total less than 20 mebibytes. |
| 53 | +-- - Let @grossRequest@ be the oldest block on @theCandidate@ that has not |
| 54 | +-- already been downloaded. |
58 | 55 | -- |
59 | 56 | -- - If @grossRequest@ is empty, then terminate this iteration. Otherwise, |
60 | | --- pick the best peer (according to @peersOrder@) offering all of the |
61 | | --- blocks in @grossRequest@. |
| 57 | +-- pick the best peer (according to @peersOrder@) offering the |
| 58 | +-- block in @grossRequest@. |
62 | 59 | -- |
63 | | --- 3. Craft that actual request to @thePeer@ asking blocks of @theCandidate@: |
| 60 | +-- 3. Craft the actual request to @thePeer@ asking blocks of @theCandidate@: |
64 | 61 | -- |
65 | 62 | -- - If the byte size of @inflight(thePeer)@ is below the low-water mark, |
66 | 63 | -- then terminate this iteration. |
|
69 | 66 | -- which blocks are actually already currently in-flight with @thePeer@. |
70 | 67 | -- |
71 | 68 | -- 4. If we went through the election of a new peer, replace @currentPeer@ and |
72 | | --- reset @currentStart@. REVIEW: Maybe this should just be done directly in |
73 | | --- step 2. |
| 69 | +-- reset @currentStart@. |
74 | 70 | -- |
75 | 71 | -- Terminate this iteration. |
76 | 72 | -- |
77 | | --- About ignored in-flight requests |
78 | | --- -------------------------------- |
| 73 | +-- About the influence of in-flight requests |
| 74 | +-- ----------------------------------------- |
79 | 75 | -- |
80 | 76 | -- One can note that in-flight requests are ignored when finding a new peer, but |
81 | 77 | -- considered when crafting the actual request to a chosen peer. This is by |
82 | | --- design. The goal of this algorithm is to keep talking to the same peer unless |
83 | | --- it proves to be too weak; in that case, @inflight(p)@ will be empty for all |
84 | | --- @p /= currentPeer@. |
| 78 | +-- design. We explain the rationale here. |
85 | 79 | -- |
86 | 80 | -- If a peer proves too slow, then we give up on it (see point 0. above), even |
87 | 81 | -- if it has requests in-flight. In subsequent selections of peers (point 2.), |
|
102 | 96 | -- Interactions with ChainSync Jumping (CSJ) |
103 | 97 | -- ----------------------------------------- |
104 | 98 | -- |
105 | | --- This decision logic is not so obviously coupled with CSJ, but it is in some |
106 | | --- subtle ways: |
107 | | --- |
108 | | --- - Because we always require our peers to be able to serve a gross request of |
109 | | --- oldest blocks, peers with longer chains have a better chance to pass this |
110 | | --- criteria and to be selected as current peer. The CSJ dynamo, being always |
111 | | --- ahead of jumpers, has therefore more chances to be selected as the current |
112 | | --- peer. It is still possible for a jumper or a disengaged peer to be |
113 | | --- selected. |
| 99 | +-- Because we always require our peers to be able to serve a gross request |
| 100 | +-- with an old block, peers with longer chains have a better chance to pass |
| 101 | +-- this criteria and to be selected as current peer. The CSJ dynamo, being |
| 102 | +-- always ahead of jumpers, has therefore more chances to be selected as the |
| 103 | +-- current peer. It is still possible for a jumper or a disengaged peer to be |
| 104 | +-- selected. |
114 | 105 | -- |
115 | | --- - If the current peer is the CSJ dynamo, but it is a dishonest peer serving |
116 | | --- headers fast but retaining blocks, it might be able to drastically leash |
117 | | --- us, because its ChainSync client will be stuck behind the forecast horizon |
118 | | --- (and therefore not subject to ChainSync punishments such as the Limit on |
119 | | --- Patience). This is why we need to consider starvation of ChainSel and |
120 | | --- demote peers that let us starve. |
| 106 | +-- If the current peer is the CSJ dynamo and it is a dishonest peer that retains |
| 107 | +-- blocks, it will get multiple opportunities to do so since it will be selected |
| 108 | +-- as the current peer more often. We therefore rotate the dynamo every time it |
| 109 | +-- is the current peer and it fails to serve blocks promptly. |
121 | 110 | -- |
122 | 111 | -- About the gross request |
123 | 112 | -- ----------------------- |
124 | 113 | -- |
125 | | --- Morally, we want to select a peer that is able to serve us a batch of oldest |
126 | | --- blocks of @theCandidate@. However, the actual requests depend not only on the |
127 | | --- size of the blocks to fetch, but also on the network performances of the peer |
128 | | --- and what requests it already has in-flight. Looking at what peer can create |
129 | | --- an actual request for @theCandidate@ can be misleading: indeed, our |
130 | | --- @currentPeer@ might not be able to create a request simply because it is |
131 | | --- already busy answering other requests from us. This calls for the |
132 | | --- introduction of an objective criterium, which the gross request provides. |
| 114 | +-- We want to select a peer that is able to serve us a batch of oldest blocks |
| 115 | +-- of @theCandidate@. However, not every peer will be able to deliver these |
| 116 | +-- batches as they might be on different chains. We therefore select a peer only |
| 117 | +-- if its candidate fragment contains the block in the gross request. In this |
| 118 | +-- way, we ensure that the peer can serve at least one block that we wish to |
| 119 | +-- fetch. |
133 | 120 | -- |
134 | | --- If the gross request is included in a peer's candidate, it means that this |
135 | | --- peer can serve at least 1 block that we wish to fetch. The actual request might |
136 | | --- be bigger than that because the peer can have more blocks. |
| 121 | +-- If the peer cannot offer any more blocks after that, it will be rotated out |
| 122 | +-- soon. |
137 | 123 | -- |
138 | 124 | module Ouroboros.Network.BlockFetch.Decision.BulkSync ( |
139 | 125 | fetchDecisionsBulkSyncM |
@@ -419,7 +405,6 @@ selectTheCandidate |
419 | 405 | -- consider longest fragments first. |
420 | 406 | . List.sortOn (Down . headBlockNo . fst) |
421 | 407 | where |
422 | | - -- Very ad-hoc helper. |
423 | 408 | -- Write all of the declined peers, and find the candidate fragment |
424 | 409 | -- if there is any. |
425 | 410 | separateDeclinedAndStillInRace :: |
@@ -485,8 +470,8 @@ selectThePeer |
485 | 470 | Right () -> return $ Just (thePeerCandidate, thePeerInfo) |
486 | 471 |
|
487 | 472 | Nothing -> do |
488 | | - -- For each peer, check whether its candidate contains the gross request in |
489 | | - -- its entirety, otherwise decline it. This will guarantee that the |
| 473 | + -- For each peer, check whether its candidate contains the head of the |
| 474 | + -- gross request, otherwise decline it. This will guarantee that the |
490 | 475 | -- remaining peers can serve the refined request that we will craft later. |
491 | 476 | peers <- |
492 | 477 | filterM |
|
0 commit comments