implement send-receive-acknowledge-retry loop for requests
This commit is contained in:
		
							parent
							
								
									0e6f126b3b
								
							
						
					
					
						commit
						8d18f952cd
					
				
					 1 changed files with 54 additions and 24 deletions
				
			
		| 
						 | 
					@ -121,41 +121,71 @@ markCacheEntryAsVerified timestamp = Map.adjust adjustFunc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- ====== message send and receive operations ======
 | 
					-- ====== message send and receive operations ======
 | 
				
			||||||
 | 
					
 | 
				
			||||||
requestQueryID :: NodeState -> NodeID -> IO NodeState
 | 
					--requestQueryID :: NodeState -> NodeID -> IO NodeState
 | 
				
			||||||
-- 1. do a local lookup for the l closest nodes
 | 
					---- 1. do a local lookup for the l closest nodes
 | 
				
			||||||
-- 2. create l sockets
 | 
					---- 2. create l sockets
 | 
				
			||||||
-- 3. send a message async concurrently to all l nodes
 | 
					---- 3. send a message async concurrently to all l nodes
 | 
				
			||||||
-- 4. collect the results, insert them into cache
 | 
					---- 4. collect the results, insert them into cache
 | 
				
			||||||
-- 5. repeat until FOUND (problem: new entries not necessarily already in cache, explicitly compare with closer results)
 | 
					---- 5. repeat until FOUND (problem: new entries not necessarily already in cache, explicitly compare with closer results)
 | 
				
			||||||
requestQueryID ns targetID = do
 | 
					--requestQueryID ns targetID = do
 | 
				
			||||||
    cacheSnapshot <- readIORef $ getNodeCacheRef ns
 | 
					--    cacheSnapshot <- readIORef $ getNodeCacheRef ns
 | 
				
			||||||
    let localResult = queryLocalCache ns cacheSnapshot (fromMaybe 1 $ getLNumBestNodes ns) targetID
 | 
					--    let localResult = queryLocalCache ns cacheSnapshot (fromMaybe 1 $ getLNumBestNodes ns) targetID
 | 
				
			||||||
    -- FOUND can only be returned if targetID is owned by local node
 | 
					--    -- FOUND can only be returned if targetID is owned by local node
 | 
				
			||||||
    case localResult of
 | 
					--    case localResult of
 | 
				
			||||||
      FOUND thisNode -> return thisNode
 | 
					--      FOUND thisNode -> return thisNode
 | 
				
			||||||
      FORWARD nodeSet ->
 | 
					--      FORWARD nodeSet ->
 | 
				
			||||||
          sockets <- mapM (\resultNode -> mkSendSocket (domain result) (dhtPort resultNode)) $ Set.toList nodeSet
 | 
					--          sockets <- mapM (\resultNode -> mkSendSocket (domain result) (dhtPort resultNode)) $ Set.toList nodeSet
 | 
				
			||||||
          -- ToDo: process results immediately instead of waiting for the last one to finish, see https://stackoverflow.com/a/38815224/9198613
 | 
					--          -- ToDo: process results immediately instead of waiting for the last one to finish, see https://stackoverflow.com/a/38815224/9198613
 | 
				
			||||||
          responses = mapM 
 | 
					--          responses = mapM 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- | Generic function for sending a request over a connected socket and collecting the response.
 | 
				
			||||||
 | 
					-- Serialises the message and tries to deliver its parts for a number of attempts within a specified timeout.
 | 
				
			||||||
sendRequestTo :: Int                    -- ^ timeout in seconds
 | 
					sendRequestTo :: Int                    -- ^ timeout in seconds
 | 
				
			||||||
              -> Int                    -- ^ number of retries
 | 
					              -> Int                    -- ^ number of retries
 | 
				
			||||||
              -> FediChordMessage       -- ^ the message to be sent
 | 
					              -> FediChordMessage       -- ^ the message to be sent
 | 
				
			||||||
              -> Socket                 -- ^ connected socket to use for sending
 | 
					              -> Socket                 -- ^ connected socket to use for sending
 | 
				
			||||||
              -> IO (Set.Set FediChordMessage)  -- ^ responses
 | 
					              -> IO (Set.Set FediChordMessage)  -- ^ responses
 | 
				
			||||||
sendRequestTo timeout attempts msg sock = do
 | 
					sendRequestTo timeoutMillis numAttempts msg sock = do
 | 
				
			||||||
    let requests = serialiseMessage 1200 msg
 | 
					    let requests = serialiseMessage 1200 msg
 | 
				
			||||||
 | 
					    -- create a queue for passing received response messages back, even after a timeout
 | 
				
			||||||
 | 
					    responseQ <- newTBQueueIO $ 2*maximumParts  -- keep room for duplicate packets
 | 
				
			||||||
 | 
					    -- start sendAndAck with timeout
 | 
				
			||||||
    -- ToDo: make attempts and timeout configurable
 | 
					    -- ToDo: make attempts and timeout configurable
 | 
				
			||||||
    attempts 3 . timeout 5000 $ do
 | 
					    attempts numAttempts . timeout timeoutMillis $ sendAndAck responseQ sock requests
 | 
				
			||||||
 | 
					    -- after timeout, check received responses, delete them from unacked message set/ map and rerun senAndAck with that if necessary.
 | 
				
			||||||
 | 
					    recvdParts <- atomically $ flushTBQueue responseQ
 | 
				
			||||||
 | 
					    -- PLACEHOLDER
 | 
				
			||||||
 | 
					    pure Set.empty
 | 
				
			||||||
  where
 | 
					  where
 | 
				
			||||||
    -- state reingeben: state = noch nicht geackte messages, result = responses
 | 
					    -- state reingeben: state = noch nicht geackte messages, result = responses
 | 
				
			||||||
    sendAndAck :: Socket -> StateT (Map.Map Integer BS.ByteString) IO (Set.Set FediChordMessage)
 | 
					    sendAndAck :: TBQueue FediChordMessage          -- ^ the queue for putting in the received responses
 | 
				
			||||||
    sendAndAck sock = do
 | 
					               -> Socket                            -- ^ the socket used for sending and receiving for this particular remote node
 | 
				
			||||||
        remainingSends <- get
 | 
					               -> Map.Map Integer BS.ByteString     -- ^ the remaining unacked request parts
 | 
				
			||||||
 | 
					               -> IO ()
 | 
				
			||||||
 | 
					    sendAndAck responseQueue sock remainingSends = do
 | 
				
			||||||
        sendMany sock $ Map.elems remainingSends
 | 
					        sendMany sock $ Map.elems remainingSends
 | 
				
			||||||
        -- timeout pro receive socket, danach catMaybes
 | 
					        -- if all requests have been acked/ responded to, return prematurely
 | 
				
			||||||
        -- wichtig: Pakete können dupliziert werden, dh es können mehr ACKs als gesendete parts ankommen
 | 
					        recvLoop responseQueue remainingSends Set.empty
 | 
				
			||||||
        replicateM
 | 
					    recvLoop :: TBQueue FediChordMessage          -- ^ the queue for putting in the received responses
 | 
				
			||||||
 | 
					             -> Map.Map Integer BS.ByteString       -- ^ the remaining unacked request parts
 | 
				
			||||||
 | 
					             -> Set.Set Integer                     -- ^ already received response part numbers
 | 
				
			||||||
 | 
					             -> IO ()
 | 
				
			||||||
 | 
					    recvLoop responseQueue remainingSends' receivedPartNums = do
 | 
				
			||||||
 | 
					        -- 65535 is maximum length of UDP packets, as long as
 | 
				
			||||||
 | 
					        -- no IPv6 jumbograms are used
 | 
				
			||||||
 | 
					        response <- deserialiseMessage <$> recv sock 65535
 | 
				
			||||||
 | 
					        case response of
 | 
				
			||||||
 | 
					          -- drop errors
 | 
				
			||||||
 | 
					          Left _ -> recvLoop responseQueue remainingSends' receivedPartNums
 | 
				
			||||||
 | 
					          Right msg -> do
 | 
				
			||||||
 | 
					              atomically $ writeTBQueue responseQueue msg
 | 
				
			||||||
 | 
					              let
 | 
				
			||||||
 | 
					                newRemaining = Map.delete (part msg) remainingSends'
 | 
				
			||||||
 | 
					                newReceivedParts = Set.insert (part msg) receivedPartNums
 | 
				
			||||||
 | 
					              -- ToDo: handle responses with more parts than the request
 | 
				
			||||||
 | 
					              if Map.null newRemaining && Set.size receivedPartNums == fromIntegral (parts msg)
 | 
				
			||||||
 | 
					                 then pure ()
 | 
				
			||||||
 | 
					                 else recvLoop responseQueue newRemaining receivedPartNums
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue