diff options
author | Joe Crayne <joe@jerkface.net> | 2018-12-16 13:51:02 -0500 |
---|---|---|
committer | Joe Crayne <joe@jerkface.net> | 2018-12-16 14:08:27 -0500 |
commit | 688450c2d9998db7b4389dc9642d219774c23857 (patch) | |
tree | 69933330ad0baba2eb115d70d1e63cb085664ea1 /src/Network/Kademlia/Search.hs | |
parent | 8a72757ae66f6ec013b8f3443aea6f3266a2ab26 (diff) |
More TCP work.
Diffstat (limited to 'src/Network/Kademlia/Search.hs')
-rw-r--r-- | src/Network/Kademlia/Search.hs | 62 |
1 files changed, 26 insertions, 36 deletions
diff --git a/src/Network/Kademlia/Search.hs b/src/Network/Kademlia/Search.hs index d3aaae28..e87a8618 100644 --- a/src/Network/Kademlia/Search.hs +++ b/src/Network/Kademlia/Search.hs | |||
@@ -32,36 +32,38 @@ data Search nid addr tok ni r = Search | |||
32 | { searchSpace :: KademliaSpace nid ni | 32 | { searchSpace :: KademliaSpace nid ni |
33 | , searchNodeAddress :: ni -> addr | 33 | , searchNodeAddress :: ni -> addr |
34 | , searchQuery :: nid -> ni -> IO (Maybe ([ni], [r], Maybe tok)) | 34 | , searchQuery :: nid -> ni -> IO (Maybe ([ni], [r], Maybe tok)) |
35 | , searchAlpha :: Int -- α = 8 | ||
36 | -- | 'searchK' should be larger than 'searchAlpha'. How much larger depends on | ||
37 | -- how fast the queries are. For Tox's much slower onion-routed queries, we | ||
38 | -- need to ensure that closer non-responding queries don't completely push out | ||
39 | -- farther away queries. | ||
40 | -- | ||
41 | -- For BitTorrent, setting them both 8 was not an issue, but that is no longer | ||
42 | -- supported because now the number of remembered informants is now the | ||
43 | -- difference between these two numbers. So, if searchK = 16 and searchAlpha = | ||
44 | -- 4, then the number of remembered query responses is 12. | ||
45 | , searchK :: Int -- K = 16 | ||
35 | } | 46 | } |
36 | 47 | ||
37 | data SearchState nid addr tok ni r = SearchState | 48 | data SearchState nid addr tok ni r = SearchState |
38 | {- | ||
39 | { searchParams :: Search nid addr ni r | ||
40 | |||
41 | , searchTarget :: nid | ||
42 | -- | This action will be performed at least once on each search result. | ||
43 | -- It may be invoked multiple times since different nodes may report the | ||
44 | -- same result. If the action returns 'False', the search will be | ||
45 | -- aborted, otherwise it will continue until it is decided that we've | ||
46 | -- asked the closest K nodes to the target. | ||
47 | , searchResult :: r -> STM Bool | ||
48 | |||
49 | -} | ||
50 | |||
51 | { -- | The number of pending queries. Incremented before any query is sent | 49 | { -- | The number of pending queries. Incremented before any query is sent |
52 | -- and decremented when we get a reply. | 50 | -- and decremented when we get a reply. |
53 | searchPendingCount :: TVar Int | 51 | searchPendingCount :: TVar Int |
54 | -- | Nodes scheduled to be queried. | 52 | -- | Nodes scheduled to be queried (roughly at most K). |
55 | , searchQueued :: TVar (MinMaxPSQ ni nid) | 53 | , searchQueued :: TVar (MinMaxPSQ ni nid) |
56 | -- | The nearest (K - α) nodes that issued a reply. | 54 | -- | The nearest (K - α) nodes that issued a reply. |
55 | -- | ||
56 | -- α is the maximum number of simultaneous queries. | ||
57 | , searchInformant :: TVar (MinMaxPSQ' ni nid (Maybe tok)) | 57 | , searchInformant :: TVar (MinMaxPSQ' ni nid (Maybe tok)) |
58 | -- | This tracks already-queried addresses so we avoid bothering them | 58 | -- | This tracks already-queried addresses so we avoid bothering them |
59 | -- again. XXX: We could probably keep only the pending queries in this | 59 | -- again. XXX: We could probably keep only the pending queries in this |
60 | -- set. It also can be a bounded 'MinMaxPSQ', although searchAlpha | 60 | -- set. It also can be a bounded 'MinMaxPSQ', although searchAlpha |
61 | -- should limit the number of outstanding queries. | 61 | -- should limit the number of outstanding queries. |
62 | , searchVisited :: TVar (Set addr) | 62 | , searchVisited :: TVar (Set addr) |
63 | , searchSpec :: Search nid addr tok ni r | ||
63 | } | 64 | } |
64 | 65 | ||
66 | |||
65 | newSearch :: ( Ord addr | 67 | newSearch :: ( Ord addr |
66 | , PSQKey nid | 68 | , PSQKey nid |
67 | , PSQKey ni | 69 | , PSQKey ni |
@@ -77,7 +79,7 @@ newSearch :: ( Ord addr | |||
77 | -> nid | 79 | -> nid |
78 | -> [ni] -- Initial nodes to query. | 80 | -> [ni] -- Initial nodes to query. |
79 | -> STM (SearchState nid addr tok ni r) | 81 | -> STM (SearchState nid addr tok ni r) |
80 | newSearch (Search space nAddr qry) target ns = do | 82 | newSearch s@(Search space nAddr qry _ _) target ns = do |
81 | c <- newTVar 0 | 83 | c <- newTVar 0 |
82 | q <- newTVar $ MM.fromList | 84 | q <- newTVar $ MM.fromList |
83 | $ map (\n -> n :-> kademliaXor space target (kademliaLocation space n)) | 85 | $ map (\n -> n :-> kademliaXor space target (kademliaLocation space n)) |
@@ -85,7 +87,7 @@ newSearch (Search space nAddr qry) target ns = do | |||
85 | i <- newTVar MM.empty | 87 | i <- newTVar MM.empty |
86 | v <- newTVar Set.empty | 88 | v <- newTVar Set.empty |
87 | return -- (Search space nAddr qry) , r , target | 89 | return -- (Search space nAddr qry) , r , target |
88 | ( SearchState c q i v ) | 90 | ( SearchState c q i v s ) |
89 | 91 | ||
90 | -- | Discard a value from a key-priority-value tuple. This is useful for | 92 | -- | Discard a value from a key-priority-value tuple. This is useful for |
91 | -- swaping items from a "MinMaxPSQ'" to a "MinMaxPSQ". | 93 | -- swaping items from a "MinMaxPSQ'" to a "MinMaxPSQ". |
@@ -110,21 +112,6 @@ reset nearestNodes qsearch target st = do | |||
110 | writeTVar (searchPendingCount st) 0 | 112 | writeTVar (searchPendingCount st) 0 |
111 | return st | 113 | return st |
112 | 114 | ||
113 | searchAlpha :: Int | ||
114 | searchAlpha = 8 | ||
115 | |||
116 | -- | 'searchK' should be larger than 'searchAlpha'. How much larger depends on | ||
117 | -- how fast the queries are. For Tox's much slower onion-routed queries, we | ||
118 | -- need to ensure that closer non-responding queries don't completely push out | ||
119 | -- farther away queries. | ||
120 | -- | ||
121 | -- For BitTorrent, setting them both 8 was not an issue, but that is no longer | ||
122 | -- supported because now the number of remembered informants is now the | ||
123 | -- difference between these two numbers. So, if searchK = 16 and searchAlpha = | ||
124 | -- 4, then the number of remembered query responses is 12. | ||
125 | searchK :: Int | ||
126 | searchK = 16 | ||
127 | |||
128 | sendQuery :: forall addr nid tok ni r. | 115 | sendQuery :: forall addr nid tok ni r. |
129 | ( Ord addr | 116 | ( Ord addr |
130 | , PSQKey nid | 117 | , PSQKey nid |
@@ -159,8 +146,11 @@ sendQuery Search{..} searchTarget searchResult sch@SearchState{..} (ni :-> d) = | |||
159 | | otherwise = MM.insertTake k n ( kademliaXor searchSpace searchTarget | 146 | | otherwise = MM.insertTake k n ( kademliaXor searchSpace searchTarget |
160 | $ kademliaLocation searchSpace n ) | 147 | $ kademliaLocation searchSpace n ) |
161 | q | 148 | q |
149 | |||
162 | qsize0 <- MM.size <$> readTVar searchQueued | 150 | qsize0 <- MM.size <$> readTVar searchQueued |
163 | let qsize = if qsize0 < searchK then searchK else qsize0 | 151 | let qsize = if qsize0 < searchK then searchK else qsize0 -- Allow searchQueued to grow |
152 | -- only when there's fewer than | ||
153 | -- K elements. | ||
164 | modifyTVar searchQueued $ \q -> foldr (insertFoundNode qsize) q ns | 154 | modifyTVar searchQueued $ \q -> foldr (insertFoundNode qsize) q ns |
165 | modifyTVar searchInformant $ MM.insertTake' (searchK - searchAlpha) ni tok d | 155 | modifyTVar searchInformant $ MM.insertTake' (searchK - searchAlpha) ni tok d |
166 | flip fix rs $ \loop -> \case | 156 | flip fix rs $ \loop -> \case |
@@ -174,13 +164,13 @@ sendQuery Search{..} searchTarget searchResult sch@SearchState{..} (ni :-> d) = | |||
174 | searchIsFinished :: ( PSQKey nid | 164 | searchIsFinished :: ( PSQKey nid |
175 | , PSQKey ni | 165 | , PSQKey ni |
176 | ) => SearchState nid addr tok ni r -> STM Bool | 166 | ) => SearchState nid addr tok ni r -> STM Bool |
177 | searchIsFinished SearchState{ ..} = do | 167 | searchIsFinished SearchState{..} = do |
178 | q <- readTVar searchQueued | 168 | q <- readTVar searchQueued |
179 | cnt <- readTVar searchPendingCount | 169 | cnt <- readTVar searchPendingCount |
180 | informants <- readTVar searchInformant | 170 | informants <- readTVar searchInformant |
181 | return $ cnt == 0 | 171 | return $ cnt == 0 |
182 | && ( MM.null q | 172 | && ( MM.null q |
183 | || ( MM.size informants >= (searchK - searchAlpha) | 173 | || ( MM.size informants >= (searchK searchSpec - searchAlpha searchSpec) |
184 | && ( PSQ.prio (fromJust $ MM.findMax informants) | 174 | && ( PSQ.prio (fromJust $ MM.findMax informants) |
185 | <= PSQ.prio (fromJust $ MM.findMin q)))) | 175 | <= PSQ.prio (fromJust $ MM.findMin q)))) |
186 | 176 | ||
@@ -197,7 +187,7 @@ search :: | |||
197 | , Show nid | 187 | , Show nid |
198 | ) => Search nid addr tok ni r -> R.BucketList ni -> nid -> (r -> STM Bool) -> IO (SearchState nid addr tok ni r) | 188 | ) => Search nid addr tok ni r -> R.BucketList ni -> nid -> (r -> STM Bool) -> IO (SearchState nid addr tok ni r) |
199 | search sch buckets target result = do | 189 | search sch buckets target result = do |
200 | let ns = R.kclosest (searchSpace sch) searchK target buckets | 190 | let ns = R.kclosest (searchSpace sch) (searchK sch) target buckets |
201 | st <- atomically $ newSearch sch target ns | 191 | st <- atomically $ newSearch sch target ns |
202 | forkIO $ searchLoop sch target result st | 192 | forkIO $ searchLoop sch target result st |
203 | return st | 193 | return st |
@@ -218,7 +208,7 @@ searchLoop sch@Search{..} target result s@SearchState{..} = do | |||
218 | found <- MM.minView <$> readTVar searchQueued | 208 | found <- MM.minView <$> readTVar searchQueued |
219 | case found of | 209 | case found of |
220 | Just (ni :-> d, q) | 210 | Just (ni :-> d, q) |
221 | | -- If there's fewer than /k/ informants and there's any | 211 | | -- If there's fewer than /k - α/ informants and there's any |
222 | -- node we haven't yet got a response from. | 212 | -- node we haven't yet got a response from. |
223 | (MM.size informants < searchK - searchAlpha) && (cnt > 0 || not (MM.null q)) | 213 | (MM.size informants < searchK - searchAlpha) && (cnt > 0 || not (MM.null q)) |
224 | -- Or there's no informants yet at all. | 214 | -- Or there's no informants yet at all. |