summaryrefslogtreecommitdiff
path: root/src/Network/Kademlia/Search.hs
diff options
context:
space:
mode:
authorJoe Crayne <joe@jerkface.net>2018-12-16 13:51:02 -0500
committerJoe Crayne <joe@jerkface.net>2018-12-16 14:08:27 -0500
commit688450c2d9998db7b4389dc9642d219774c23857 (patch)
tree69933330ad0baba2eb115d70d1e63cb085664ea1 /src/Network/Kademlia/Search.hs
parent8a72757ae66f6ec013b8f3443aea6f3266a2ab26 (diff)
More TCP work.
Diffstat (limited to 'src/Network/Kademlia/Search.hs')
-rw-r--r--src/Network/Kademlia/Search.hs62
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
37data SearchState nid addr tok ni r = SearchState 48data 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
65newSearch :: ( Ord addr 67newSearch :: ( 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)
80newSearch (Search space nAddr qry) target ns = do 82newSearch 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
113searchAlpha :: Int
114searchAlpha = 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.
125searchK :: Int
126searchK = 16
127
128sendQuery :: forall addr nid tok ni r. 115sendQuery :: 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) =
174searchIsFinished :: ( PSQKey nid 164searchIsFinished :: ( 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
177searchIsFinished SearchState{ ..} = do 167searchIsFinished 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)
199search sch buckets target result = do 189search 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.