This commit is contained in:
voidlizard 2024-11-07 07:07:32 +03:00
parent 859c108b2a
commit ce05751f4d
13 changed files with 655 additions and 720 deletions

View File

@ -853,46 +853,3 @@ processBlock h = do
unboxBundleRef (BundleRefValue box) = unboxSignedBox0 box unboxBundleRef (BundleRefValue box) = unboxSignedBox0 box
-- NOTE: this is an adapter for a ResponseM monad
-- because response is working in ResponseM monad (ha!)
-- So don't be confused with types
--
mkAdapter :: forall e m . ( m ~ PeerM e IO
, HasProtocol e (BlockChunks e)
, Hashable (SessionKey e (BlockChunks e))
, Sessions e (BlockChunks e) (ResponseM e m)
, Typeable (SessionKey e (BlockChunks e))
, EventEmitter e (BlockChunks e) m
, Pretty (Peer e)
)
=> m (BlockChunksI e (ResponseM e m ))
mkAdapter = do
storage <- getStorage
pure $
BlockChunksI
{ blkSize = liftIO . hasBlock storage
, blkChunk = \h o s -> liftIO (getChunk storage h o s)
, blkGetHash = \c -> find (DownloadSessionKey @e c) (view sBlockHash)
, blkAcceptChunk = \(c,p,h,n,bs) -> void $ runMaybeT $ do
let cKey = DownloadSessionKey (p,c)
dodo <- lift $ find cKey (view sBlockChunks)
unless (isJust dodo) $ do
debug $ "session lost for peer !" <+> pretty p
-- debug $ "FINDING-SESSION:" <+> pretty c <+> pretty n
-- debug $ "GOT SHIT" <+> pretty c <+> pretty n
se <- MaybeT $ find cKey id
let dwnld = view sBlockChunks se
let dwnld2 = view sBlockChunks2 se
-- debug $ "WRITE SHIT" <+> pretty c <+> pretty n
liftIO $ atomically do
writeTQueue dwnld (n, bs)
modifyTVar' dwnld2 (IntMap.insert (fromIntegral n) bs)
}

View File

@ -0,0 +1,554 @@
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# Language UndecidableInstances #-}
{-# Language AllowAmbiguousTypes #-}
module BlockDownloadNew where
import HBS2.Prelude.Plated
import HBS2.OrDie
import HBS2.Data.Detect
import HBS2.Hash
import HBS2.Merkle
import HBS2.Defaults
import HBS2.Events
import HBS2.Net.Proto.Service
import HBS2.Net.Proto.Sessions
import HBS2.Base58
import HBS2.Data.Types.Peer
import HBS2.Data.Types.Refs
import HBS2.Actors.Peer
import HBS2.Peer.Proto.Peer
import HBS2.Peer.Proto.BlockInfo
import HBS2.Peer.Proto.BlockChunks
import HBS2.Peer.Brains
import HBS2.Storage
import HBS2.Storage.Operations.Missed
import HBS2.Misc.PrettyStuff
import HBS2.Clock
import HBS2.Net.Auth.Schema
import HBS2.Peer.RPC.Internal.Types
import HBS2.Peer.RPC.API.Peer
import HBS2.Net.Messaging.TCP
import PeerTypes
import PeerInfo
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Cont
import Control.Concurrent.STM (flushTQueue,retry)
import Data.Map qualified as Map
import Data.Map (Map)
import Data.HashMap.Strict (HashMap)
import Data.HashMap.Strict qualified as HM
import Data.HashSet (HashSet)
import Data.HashSet qualified as HS
import Data.IntMap qualified as IntMap
import Data.IntMap (IntMap)
import Data.List.Split qualified as Split
import Data.Text qualified as Text
import Data.Either
import Data.Maybe
import Data.ByteString.Lazy qualified as LBS
import Data.ByteString.Lazy (ByteString)
import Data.ByteString qualified as BS
import Data.List qualified as L
import Data.Coerce
import Numeric
import UnliftIO
import UnliftIO.Concurrent
import Lens.Micro.Platform
import Streaming.Prelude qualified as S
data DownloadError e =
DownloadStuckError HashRef (Peer e)
| StorageError
| UnknownPeerError (Peer e)
| InternalError Int
| PeerMissBlockError HashRef (Peer e)
| PeerBlockHashMismatch (Peer e)
| PeerRequestTimeout (Peer e)
| Incomplete HashRef
deriving stock (Generic,Typeable)
instance Pretty (Peer e) => Show (DownloadError e) where
show (DownloadStuckError h p) = show $ parens $ "DownloadStuck" <+> pretty h <+> pretty p
show (UnknownPeerError p) = show $ parens $ "UnknownPeerError" <+> pretty p
show (PeerMissBlockError h p) = show $ parens $ "PeerMissBlockError" <+> pretty h <+> pretty p
show (PeerRequestTimeout p) = show $ parens $ "PeerRequestTimeout" <+> pretty p
show (PeerBlockHashMismatch p) = show $ parens $ "PeerBlockHashMismatch" <+> pretty p
show StorageError = show "StorageError"
show (InternalError n) = show $ parens "InternalError" <+> pretty n
show (Incomplete h) = show $ parens "Incomplete" <+> pretty h
instance (Typeable e, Pretty (Peer e)) => Exception (DownloadError e)
class BlockSizeCache e cache where
cacheBlockSize :: forall m . MonadUnliftIO m
=> cache
-> PubKey 'Sign (Encryption e)
-> Hash HbSync
-> Integer
-> m ()
findBlockSize :: forall m . MonadUnliftIO m
=> cache
-> PubKey 'Sign (Encryption e)
-> Hash HbSync
-> m (Maybe Integer)
instance BlockSizeCache e () where
cacheBlockSize _ _ _ _ = pure ()
findBlockSize _ _ _ = pure Nothing
instance BlockSizeCache e (SomeBrains e) where
cacheBlockSize = brainsCacheBlockSize @e
findBlockSize = brainsFindBlockSize @e
queryBlockSizeFromPeer :: forall e cache m . ( e ~ L4Proto
, MonadUnliftIO m
, BlockSizeCache e cache
)
=> cache
-> PeerEnv e
-> Hash HbSync
-> Peer e
-> m (Either (DownloadError e) (Maybe Integer))
queryBlockSizeFromPeer cache e h peer = do
what <- try @_ @(DownloadError e) $ liftIO $ withPeerM e do
flip runContT pure $ callCC \exit -> do
PeerData{..} <- lift $ find (KnownPeerKey peer) id
>>= orThrow (UnknownPeerError peer)
s <- lift $ findBlockSize @e cache _peerSignKey h
debug $ "FOUND CACHED VALUE" <+> pretty h <+> pretty s
maybe none (exit . Just) s
lift do
sizeQ <- newTQueueIO
subscribe @e (BlockSizeEventKey peer) $ \case
BlockSizeEvent (that, hx, sz) | hx == h -> do
atomically $ writeTQueue sizeQ (Just sz)
cacheBlockSize @e cache _peerSignKey h sz
_ -> do
atomically $ writeTQueue sizeQ Nothing
request peer (GetBlockSize @e h)
race ( pause defBlockInfoTimeout ) (atomically $ readTQueue sizeQ )
>>= orThrow (PeerRequestTimeout peer)
case what of
Left{} -> pure $ Left (PeerRequestTimeout peer)
Right x -> pure (Right x)
data BurstMachine =
BurstMachine
{ _buTimeout :: Double
, _buBurstMax :: Int
, _buStepUp :: Double
, _buStepDown :: Double
, _buCurrent :: TVar Double
, _buErrors :: TVar Int
}
burstMachineAddErrors :: MonadUnliftIO m => BurstMachine -> Int -> m ()
burstMachineAddErrors BurstMachine{..} n =
atomically $ modifyTVar _buErrors (+n)
newBurstMachine :: MonadUnliftIO m
=> Double -- ^ timeout
-> Int -- ^ max burst
-> Maybe Int -- ^ start burst
-> Double -- ^ step up
-> Double -- ^ step down
-> m BurstMachine
newBurstMachine t0 buMax buStart up' down' = do
BurstMachine t0 buMax up down
<$> newTVarIO bu0
<*> newTVarIO 0
where
bu0 = realToFrac $ fromMaybe (max 2 (buMax `div` 2)) buStart
down = min 0.85 down'
up = min 0.5 up'
getCurrentBurst :: MonadUnliftIO m => BurstMachine -> m Int
getCurrentBurst BurstMachine{..} = readTVarIO _buCurrent <&> round
runBurstMachine :: MonadUnliftIO m
=> BurstMachine
-> m ()
runBurstMachine BurstMachine{..} = do
bu0 <- readTVarIO _buCurrent <&> realToFrac
let buMax = realToFrac _buBurstMax
let down = _buStepDown
let up = _buStepUp
_dEdT <- newTVarIO 0.00
_rates <- newTVarIO (mempty :: Map Double Double)
_buMaxReal <- newTVarIO buMax
pause @'Seconds (realToFrac _buTimeout)
flip runContT pure do
void $ ContT $ withAsync do
forever do
pause @'Seconds (realToFrac _buTimeout * 10)
atomically do
e <- headDef bu0 . Map.elems <$> readTVar _rates
nrates <- readTVar _rates <&> take 100 . Map.toList
writeTVar _rates (Map.fromList nrates)
modifyTVar _buMaxReal (max e)
void $ ContT $ withAsync do
forever do
pause @'Seconds 600
atomically $ writeTVar _buMaxReal buMax
void $ ContT $ withAsync do
forever do
pause @'Seconds (realToFrac _buTimeout * 2.0)
ddt <- readTVarIO _dEdT
when (ddt <= 0) do
atomically do
buMaxReal <- readTVar _buMaxReal
current <- readTVar _buCurrent
let new = min buMaxReal (current * (1.0 + up))
writeTVar _buCurrent new
flip fix 0 $ \next e1 -> do
let dt = realToFrac _buTimeout
eNew <- atomically do
e2 <- readTVar _buErrors
current <- readTVar _buCurrent
new <- if e2 > e1 then do
let d = max 2.0 (current * (1.0 - down))
nrates <- readTVar _rates <&> drop 3 . Map.toList
let newFucked = maybe d snd (headMay nrates)
writeTVar _rates (Map.fromList nrates)
pure newFucked
else
pure current -- $ min buMaxReal (current * (1.0 + up))
writeTVar _buErrors 0
writeTVar _buCurrent new
let dedt = realToFrac (e2 - e1) / realToFrac dt
writeTVar _dEdT (realToFrac dedt)
modifyTVar _rates ( Map.insertWith max dedt current )
pure e2
pause @'Seconds dt
next eNew
data S =
SInit
| SFetchQ
| SFetchPost (Hash HbSync) ByteString
| SCheckBefore
| SCheckAfter
-- | downloads block with dependencies recursively
downloadFromPeerRec :: forall e t cache m . ( e ~ L4Proto
, MonadUnliftIO m
, IsTimeout t
, BlockSizeCache e cache
)
=> Timeout t
-> Int
-> cache
-> PeerEnv e
-> Hash HbSync
-> Peer e
-> m (Either (DownloadError e) ())
downloadFromPeerRec t bu0 cache env h0 peer = do
sto <- withPeerM env getStorage
p <- newTQueueIO
q <- newTQueueIO
qq <- newTQueueIO
toq <- newTVarIO ( mempty :: [Int] )
bm <- newBurstMachine 0.5 256 (Just bu0) 0.05 0.10
flip runContT pure do
ContT $ withAsync $ forever do
join $ atomically (readTQueue p)
ContT $ withAsync $ forever do
h <- atomically (readTQueue qq)
void $ queryBlockSizeFromPeer cache env h peer
pause @'Seconds 1.5
ContT $ withAsync $ flip fix 10000000 $ \next m0 -> do
txs <- readTVarIO toq <&> L.take 1000
let m1 = fromMaybe m0 $ median txs
when ( m1 > m0 ) $ burstMachineAddErrors bm 1
pause @'Seconds 3
next m1
ContT $ withAsync $ runBurstMachine bm
flip fix SInit $ \next -> \case
SInit -> do
debug "SInit"
atomically $ writeTQueue q h0
next SCheckBefore
SCheckBefore -> do
here <- hasBlock sto h0 <&> isJust
if here then next SCheckAfter else next SFetchQ
SFetchQ -> do
debug "SFetchQ"
done <- atomically do
pe <- isEmptyTQueue p
qe <- isEmptyTQueue q
when (qe && not pe) retry
-- when (not pe) retry
pure qe
if done then
next SCheckAfter
else do
h <- atomically $ readTQueue q
mbs <- getBlock sto h
case mbs of
Just bs -> next (SFetchPost h bs)
Nothing -> none
bu <- lift $ getCurrentBurst bm
t0 <- getTimeCoarse
w <- lift $ downloadFromPeer t bu cache env (coerce h) peer
t1 <- getTimeCoarse
let dt = toMicroSeconds $ TimeoutTS (t1 - t0)
atomically $ modifyTVar toq ( dt : )
case w of
Right bs -> do
next (SFetchPost h bs)
Left e -> do
lift $ burstMachineAddErrors bm 1
err $ "DOWNLOAD ERROR" <+> viaShow e
next SFetchQ
SFetchPost h bs -> do
debug $ "SFetchPost" <+> pretty h
let parse = do
let refs = extractBlockRefs h bs
atomically $ mapM_ (writeTQueue q . coerce) refs
mapM_ (atomically . writeTQueue qq . coerce) refs
atomically $ writeTQueue p parse
next SFetchQ
SCheckAfter -> do
debug "SCheckAfter"
missed <- findMissedBlocks sto (HashRef h0)
mapM_ (atomically . writeTQueue q . coerce) missed
mapM_ (atomically . writeTQueue qq . coerce) missed
unless (L.null missed) $ next SFetchQ
pure $ Right ()
downloadFromPeer :: forall e t cache m . ( e ~ L4Proto
, MonadUnliftIO m
, IsTimeout t
, BlockSizeCache e cache
)
=> Timeout t
-> Int
-> cache
-> PeerEnv e
-> Hash HbSync
-> Peer e
-> m (Either (DownloadError e) ByteString)
downloadFromPeer t bu cache env h peer = liftIO $ withPeerM env do
pd@PeerData{..} <- find (KnownPeerKey peer) id
>>= orThrow (UnknownPeerError peer)
pinfo <- find (PeerInfoKey peer) id
>>= orThrow (UnknownPeerError peer)
rtt <- liftIO $ medianPeerRTT pinfo
<&> fmap ((*1) . realToFrac)
<&> fromMaybe 1000
<&> (/1e6)
let waity = 10 * rtt
sto <- getStorage
let chunkSize = defChunkSize
flip runContT pure $ callCC \exit -> do
size <- lift (findBlockSize @e cache _peerSignKey h)
>>= maybe (queryBlockSize exit) pure
coo <- genCookie (peer,h)
let key = DownloadSessionKey (peer, coo)
down@BlockDownload{..} <- newBlockDownload h
let chuQ = _sBlockChunks
let new = set sBlockChunkSize chunkSize
. set sBlockSize (fromIntegral size)
$ down
lift $ update @e new key id
let offsets = calcChunks size (fromIntegral chunkSize) :: [(Offset, Size)]
let chunkNums = [ 0 .. pred (length offsets) ]
let bursts = calcBursts bu chunkNums
callCC $ \exit2 -> do
_wx <- newTVarIO waity
for_ bursts $ \(i,chunkN) -> do
-- atomically $ flushTQueue chuQ
let req = BlockChunks @e coo (BlockGetChunks h chunkSize (fromIntegral i) (fromIntegral chunkN))
lift $ request peer req
t0 <- getTimeCoarse
let watchdog = fix \next -> do
wx <- readTVarIO _wx <&> realToFrac
-- debug $ "WATCHDOG" <+> pretty wx <+> pretty waity
r <- race (pause @'MilliSeconds (min wx waity)) do
void $ atomically $ readTQueue chuQ
either (const none) (const next) r
r <- liftIO $ race watchdog do
atomically do
pieces <- readTVar _sBlockChunks2
let done = and [ IntMap.member j pieces | j <- [i .. i + chunkN-1] ]
unless done retry
atomically $ flushTQueue chuQ
t1 <- getTimeCoarse
atomically do
wx0 <- readTVar _wx
let wx1 = realToFrac (t1 - t0) * 100 / 1e6 -- millis
writeTVar _wx wx1
case r of
Left{} -> exit2 (Left $ DownloadStuckError (HashRef h) peer)
_ -> pure ()
blk <- readTVarIO _sBlockChunks2
let rs = LBS.concat $ IntMap.elems blk
ha <- putBlock sto rs
-- let ha = Just $ hashObject @HbSync rs
lift $ expire @e key
case ha of
Nothing -> pure $ Left StorageError
Just h1 | h1 == h -> do
pure $ Right rs
Just h1 -> do
delBlock sto h1
pure $ Left (PeerBlockHashMismatch peer)
where
queryBlockSize exit = do
what <- lift $ queryBlockSizeFromPeer cache env h peer
case what of
Left{} -> exit (Left (PeerRequestTimeout peer))
Right Nothing -> exit (Left (PeerMissBlockError (HashRef h) peer))
Right (Just s) -> pure s
downloadDispatcher :: forall e m . ( e ~ L4Proto
, MonadUnliftIO m
)
=> PeerEnv e
-> m ()
downloadDispatcher env = flip runContT pure do
pts <- newTVarIO ( mempty :: HashMap (Peer e) (Async ()) )
ContT $ bracket none $ const do
readTVarIO pts >>= mapM_ cancel
atomically $ writeTVar pts mempty
liftIO $ withPeerM env do
subscribe @e DownloadReqKey $ \(DownloadReqData h) -> do
debug $ green "Download request" <+> pretty h
pause @'Seconds 1
ContT $ withAsync $ withPeerM env $ forever do
pips <- getKnownPeers @e
for_ pips $ \p -> do
debug $ "downloadDispatcher: knownPeer" <+> yellow (pretty p)
pause @'Seconds 5
forever do
pause @'Seconds 10
debug $ yellow $ "I'm download dispatcher"

View File

@ -13,8 +13,6 @@ import HBS2.Net.Proto.Types
import PeerTypes import PeerTypes
import PeerConfig import PeerConfig
import CheckPeer (peerBanned) import CheckPeer (peerBanned)
import BlockDownload()
import DownloadQ()
import Control.Monad.Trans.Maybe import Control.Monad.Trans.Maybe
import Control.Monad.Reader import Control.Monad.Reader
@ -90,13 +88,13 @@ checkBlockAnnounce :: forall e m . ( e ~ L4Proto
, m ~ PeerM e IO , m ~ PeerM e IO
) )
=> PeerConfig => PeerConfig
-> DownloadEnv e -> PeerEnv e
-> PeerNonce -> PeerNonce
-> PeerAddr e -> PeerAddr e
-> Hash HbSync -> Hash HbSync
-> m () -> m ()
checkBlockAnnounce conf denv nonce pa h = void $ runMaybeT do checkBlockAnnounce conf penv nonce pa h = void $ runMaybeT do
accept <- lift $ acceptAnnouncesFromPeer conf pa accept <- lift $ acceptAnnouncesFromPeer conf pa
@ -108,8 +106,6 @@ checkBlockAnnounce conf denv nonce pa h = void $ runMaybeT do
guard accept guard accept
lift do liftIO $ withPeerM penv do
withDownload denv $ do addDownload @e Nothing h
-- TODO: use-brains-to-download-direct
addDownload Nothing h

View File

@ -1,53 +0,0 @@
{-# Language AllowAmbiguousTypes #-}
module DownloadQ where
import HBS2.Prelude
import HBS2.Clock
import HBS2.Events
import HBS2.Data.Types.Refs
import HBS2.Net.PeerLocator
import HBS2.Peer.Brains
import HBS2.Storage
import HBS2.Storage.Operations.Missed
import PeerTypes
import PeerConfig
import Data.Foldable
import Control.Monad
import Lens.Micro.Platform
downloadQueue :: forall e m . ( MyPeer e
, DownloadFromPeerStuff e m
, HasPeerLocator e (BlockDownloadM e m)
, HasPeerLocator e m
, EventListener e (DownloadReq e) m
, HasStorage m
) => PeerConfig
-> SomeBrains e
-> DownloadEnv e -> m ()
downloadQueue _ brains denv = do
debug "DownloadQ started"
down <- listDownloads @e brains
sto <- getStorage
withDownload denv do
forM_ down $ \(HashRef h,_) -> do
missed <- findMissedBlocks sto (HashRef h)
for_ missed $ \h -> do
debug $ "DownloadQ:" <+> pretty h
addDownload mzero (fromHashRef h)
-- FIXME: timeout-hardcodes
let refs = listDownloads @e brains <&> fmap (set _2 30)
polling (Polling 5 20) refs $ \ref -> do
missed <- findMissedBlocks sto ref
trace $ "DownloadQ. check" <+> pretty ref <+> pretty (length missed)
when (null missed) do
delDownload @e brains ref

View File

@ -2,28 +2,27 @@ module Fetch where
import HBS2.Prelude import HBS2.Prelude
import HBS2.Actors.Peer import HBS2.Actors.Peer
import HBS2.Events
import HBS2.Data.Types.Refs import HBS2.Data.Types.Refs
import HBS2.Storage.Operations.Missed import HBS2.Storage.Operations.Missed
import HBS2.Net.Proto.Types import HBS2.Net.Proto.Types
import PeerTypes import PeerTypes
import BlockDownload
import Data.Foldable (for_) fetchHash :: forall e m . ( e ~ L4Proto
, MonadIO m
fetchHash :: forall e m . (e ~ L4Proto, MonadIO m) )
=> PeerEnv e => PeerEnv e
-> DownloadEnv e
-> HashRef -> HashRef
-> m () -> m ()
fetchHash penv denv href = do fetchHash penv href = do
debug $ "fetchAction" <+> pretty h debug $ "fetchAction" <+> pretty h
liftIO $ withPeerM penv $ do liftIO $ withPeerM penv $ do
sto <- getStorage sto <- getStorage
missed <- findMissedBlocks sto href missed <- findMissedBlocks sto href
for_ missed $ \miss -> do for_ missed $ \miss -> do
withDownload denv (addDownload Nothing (fromHashRef miss)) addDownload @e Nothing (fromHashRef miss)
where where
h = fromHashRef href h = fromHashRef href

View File

@ -84,9 +84,9 @@ httpWorker :: forall e s m . ( MyPeer e
, m ~ PeerM e IO , m ~ PeerM e IO
, e ~ L4Proto , e ~ L4Proto
-- , ForLWWRefProto e -- , ForLWWRefProto e
) => PeerConfig -> AnnMetaData -> DownloadEnv e -> m () ) => PeerConfig -> AnnMetaData -> m ()
httpWorker (PeerConfig syn) pmeta e = do httpWorker (PeerConfig syn) pmeta = do
sto <- getStorage sto <- getStorage
let port' = runReader (cfgValue @PeerHttpPortKey) syn <&> fromIntegral let port' = runReader (cfgValue @PeerHttpPortKey) syn <&> fromIntegral

View File

@ -42,7 +42,6 @@ import HBS2.Misc.PrettyStuff
import Brains import Brains
import PeerConfig import PeerConfig
import PeerTypes import PeerTypes
import BlockDownload()
import DBPipe.SQLite as Q import DBPipe.SQLite as Q
@ -125,7 +124,6 @@ instance ForMailbox s => Hashable (MailboxDownload s)
data MailboxProtoWorker (s :: CryptoScheme) e = data MailboxProtoWorker (s :: CryptoScheme) e =
MailboxProtoWorker MailboxProtoWorker
{ mpwPeerEnv :: PeerEnv e { mpwPeerEnv :: PeerEnv e
, mpwDownloadEnv :: DownloadEnv e
, mpwStorage :: AnyStorage , mpwStorage :: AnyStorage
, mpwCredentials :: PeerCredentials s , mpwCredentials :: PeerCredentials s
, mpwFetchQ :: TVar (HashSet (MailboxRefKey s)) , mpwFetchQ :: TVar (HashSet (MailboxRefKey s))
@ -457,7 +455,7 @@ startDownloadStuff :: forall s e m . (ForMailbox s, s ~ Encryption e, MyPeer e,
-> m () -> m ()
startDownloadStuff MailboxProtoWorker{..} href = do startDownloadStuff MailboxProtoWorker{..} href = do
liftIO $ withPeerM mpwPeerEnv $ withDownload mpwDownloadEnv liftIO $ withPeerM mpwPeerEnv
$ do $ do
debug $ "startDownloadStuff" <+> pretty href debug $ "startDownloadStuff" <+> pretty href
addDownload @e Nothing (coerce href) addDownload @e Nothing (coerce href)
@ -552,13 +550,12 @@ createMailboxProtoWorker :: forall s e m . ( MonadIO m
) )
=> PeerCredentials s => PeerCredentials s
-> PeerEnv e -> PeerEnv e
-> DownloadEnv e
-> AnyStorage -> AnyStorage
-> m (MailboxProtoWorker s e) -> m (MailboxProtoWorker s e)
createMailboxProtoWorker pc pe de sto = do createMailboxProtoWorker pc pe sto = do
-- FIXME: queue-size-hardcode -- FIXME: queue-size-hardcode
-- $class: hardcode -- $class: hardcode
MailboxProtoWorker pe de sto pc MailboxProtoWorker pe sto pc
<$> newTVarIO mempty <$> newTVarIO mempty
<*> newTBQueueIO 8000 <*> newTBQueueIO 8000
<*> newTVarIO mempty <*> newTVarIO mempty

View File

@ -44,10 +44,9 @@ import Brains
import BrainyPeerLocator import BrainyPeerLocator
import ByPassWorker import ByPassWorker
import PeerTypes hiding (info) import PeerTypes hiding (info)
import BlockDownload import BlockDownloadNew
import CheckBlockAnnounce (checkBlockAnnounce) import CheckBlockAnnounce (checkBlockAnnounce)
import CheckPeer (peerBanned) import CheckPeer (peerBanned)
import DownloadQ
import PeerInfo import PeerInfo
import PeerConfig import PeerConfig
import Bootstrap import Bootstrap
@ -836,11 +835,6 @@ runPeer opts = respawnOnError opts $ do
brainsThread <- async $ runBasicBrains conf brains brainsThread <- async $ runBasicBrains conf brains
denv <- newDownloadEnv brains
dProbe <- newSimpleProbe "BlockDownload"
addProbe dProbe
downloadEnvSetProbe denv dProbe
pl <- AnyPeerLocator <$> newBrainyPeerLocator @e (SomeBrains @e brains) mempty pl <- AnyPeerLocator <$> newBrainyPeerLocator @e (SomeBrains @e brains) mempty
@ -956,7 +950,7 @@ runPeer opts = respawnOnError opts $ do
pause @'Seconds 600 pause @'Seconds 600
liftIO $ Cache.purgeExpired nbcache liftIO $ Cache.purgeExpired nbcache
rce <- refChanWorkerEnv conf penv denv refChanNotifySource rce <- refChanWorkerEnv conf penv refChanNotifySource
rcwProbe <- newSimpleProbe "RefChanWorker" rcwProbe <- newSimpleProbe "RefChanWorker"
addProbe rcwProbe addProbe rcwProbe
@ -987,7 +981,7 @@ runPeer opts = respawnOnError opts $ do
rcw <- async $ liftIO $ runRefChanRelyWorker rce refChanAdapter rcw <- async $ liftIO $ runRefChanRelyWorker rce refChanAdapter
mailboxWorker <- createMailboxProtoWorker pc penv denv (AnyStorage s) mailboxWorker <- createMailboxProtoWorker pc penv (AnyStorage s)
p <- newSimpleProbe "MailboxProtoWorker" p <- newSimpleProbe "MailboxProtoWorker"
mailboxProtoWorkerSetProbe mailboxWorker p mailboxProtoWorkerSetProbe mailboxWorker p
@ -1002,7 +996,7 @@ runPeer opts = respawnOnError opts $ do
when (Set.member pk helpFetchKeys) do when (Set.member pk helpFetchKeys) do
liftIO $ Cache.insert nbcache (p,h) () liftIO $ Cache.insert nbcache (p,h) ()
-- debug $ "onNoBlock" <+> pretty p <+> pretty h -- debug $ "onNoBlock" <+> pretty p <+> pretty h
withPeerM penv $ withDownload denv (addDownload mzero h) liftIO $ withPeerM penv $ addDownload @e mzero h
loop <- liftIO $ async do loop <- liftIO $ async do
@ -1014,10 +1008,10 @@ runPeer opts = respawnOnError opts $ do
let doDownload h = do let doDownload h = do
pro <- isReflogProcessed @e brains h pro <- isReflogProcessed @e brains h
if pro then do if pro then do
withPeerM penv $ withDownload denv (addDownload mzero h) withPeerM penv $ addDownload @e mzero h
else do else do
-- FIXME: separate-process-to-mark-logs-processed -- FIXME: separate-process-to-mark-logs-processed
withPeerM penv $ withDownload denv (addDownload Nothing h) withPeerM penv $ addDownload @e Nothing h
let doFetchRef puk = do let doFetchRef puk = do
withPeerM penv $ do withPeerM penv $ do
@ -1064,7 +1058,7 @@ runPeer opts = respawnOnError opts $ do
subscribe @e BlockAnnounceInfoKey $ \(BlockAnnounceEvent p bi no) -> do subscribe @e BlockAnnounceInfoKey $ \(BlockAnnounceEvent p bi no) -> do
pa <- toPeerAddr p pa <- toPeerAddr p
checkBlockAnnounce conf denv no pa (view biHash bi) checkBlockAnnounce conf penv no pa (view biHash bi)
subscribe @e AnyKnownPeerEventKey $ \(KnownPeerEvent p pd) -> do subscribe @e AnyKnownPeerEventKey $ \(KnownPeerEvent p pd) -> do
@ -1169,7 +1163,7 @@ runPeer opts = respawnOnError opts $ do
let lwwRefProtoA = lwwRefProto (LWWRefProtoAdapter { lwwFetchBlock = download }) let lwwRefProtoA = lwwRefProto (LWWRefProtoAdapter { lwwFetchBlock = download })
where download h = withPeerM env $ withDownload denv (addDownload Nothing h) where download h = liftIO $ withPeerM env $ addDownload @e Nothing h
flip runContT pure do flip runContT pure do
@ -1180,7 +1174,7 @@ runPeer opts = respawnOnError opts $ do
peerThread "byPassWorker" (byPassWorker byPass) peerThread "byPassWorker" (byPassWorker byPass)
peerThread "httpWorker" (httpWorker conf peerMeta denv) peerThread "httpWorker" (httpWorker conf peerMeta)
metricsProbe <- newSimpleProbe "ghc.runtime" metricsProbe <- newSimpleProbe "ghc.runtime"
addProbe metricsProbe addProbe metricsProbe
@ -1203,9 +1197,7 @@ runPeer opts = respawnOnError opts $ do
peerThread "pexLoop" (pexLoop @e brains tcp) peerThread "pexLoop" (pexLoop @e brains tcp)
-- FIXME: new-download-loop -- FIXME: new-download-loop
peerThread "blockDownloadLoop" (blockDownloadLoop denv) peerThread "downloadDispatcher" (downloadDispatcher env)
peerThread "blockDownloadQ" (downloadQueue conf (SomeBrains brains) denv)
peerThread "fillPeerMeta" (fillPeerMeta tcp tcpProbeWait) peerThread "fillPeerMeta" (fillPeerMeta tcp tcpProbeWait)
@ -1227,7 +1219,7 @@ runPeer opts = respawnOnError opts $ do
liftIO $ withPeerM penv do liftIO $ withPeerM penv do
runProto @e runProto @e
[ makeResponse (blockSizeProto blk (downloadOnBlockSize denv) onNoBlock) [ makeResponse (blockSizeProto blk onNoBlock)
, makeResponse (blockChunksProto adapter) , makeResponse (blockChunksProto adapter)
, makeResponse blockAnnounceProto , makeResponse blockAnnounceProto
, makeResponse (withCredentials @e pc . peerHandShakeProto hshakeAdapter penv) , makeResponse (withCredentials @e pc . peerHandShakeProto hshakeAdapter penv)
@ -1300,7 +1292,7 @@ runPeer opts = respawnOnError opts $ do
subscribe @e BlockAnnounceInfoKey $ \(BlockAnnounceEvent p bi no) -> do subscribe @e BlockAnnounceInfoKey $ \(BlockAnnounceEvent p bi no) -> do
unless (p == self) do unless (p == self) do
pa <- toPeerAddr p pa <- toPeerAddr p
checkBlockAnnounce conf denv no pa (view biHash bi) checkBlockAnnounce conf penv no pa (view biHash bi)
subscribe @e PeerAnnounceEventKey $ \pe@(PeerAnnounceEvent{}) -> do subscribe @e PeerAnnounceEventKey $ \pe@(PeerAnnounceEvent{}) -> do
-- debug $ "Got peer announce!" <+> pretty pip -- debug $ "Got peer announce!" <+> pretty pip
@ -1345,7 +1337,7 @@ runPeer opts = respawnOnError opts $ do
, rpcBrains = SomeBrains brains , rpcBrains = SomeBrains brains
, rpcByPassInfo = liftIO (getStat byPass) , rpcByPassInfo = liftIO (getStat byPass)
, rpcProbes = probes , rpcProbes = probes
, rpcDoFetch = liftIO . fetchHash penv denv , rpcDoFetch = liftIO . fetchHash penv
, rpcDoRefChanHeadPost = refChanHeadPostAction , rpcDoRefChanHeadPost = refChanHeadPostAction
, rpcDoRefChanPropose = refChanProposeAction , rpcDoRefChanPropose = refChanProposeAction
, rpcDoRefChanNotify = refChanNotifyAction , rpcDoRefChanNotify = refChanNotifyAction

View File

@ -39,6 +39,7 @@ import PeerConfig
import PeerLogger import PeerLogger
import Prelude hiding (log) import Prelude hiding (log)
import Control.Monad.Trans.Maybe
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.Writer qualified as W import Control.Monad.Writer qualified as W
import Data.ByteString.Lazy (ByteString) import Data.ByteString.Lazy (ByteString)
@ -49,6 +50,7 @@ import Data.Maybe
import Lens.Micro.Platform import Lens.Micro.Platform
import Data.Hashable import Data.Hashable
import Type.Reflection import Type.Reflection
import Data.IntMap qualified as IntMap
import Data.IntMap (IntMap) import Data.IntMap (IntMap)
import Data.IntSet (IntSet) import Data.IntSet (IntSet)
import Data.Text qualified as Text import Data.Text qualified as Text
@ -122,11 +124,11 @@ instance Expires (SessionKey L4Proto (PeerInfo L4Proto)) where
expiresIn = const (Just defCookieTimeoutSec) expiresIn = const (Just defCookieTimeoutSec)
type MyPeer e = ( Eq (Peer e) type MyPeer e = ( Eq (Peer e)
, Hashable (Peer e) , Hashable (Peer e)
, Pretty (Peer e) , Pretty (Peer e)
, HasPeer e , HasPeer e
, Typeable e
, ForSignedBox (Encryption e) , ForSignedBox (Encryption e)
) )
@ -243,121 +245,19 @@ downloadMonAdd :: forall m . MonadIO m
downloadMonAdd env h whenDone = do downloadMonAdd env h whenDone = do
atomically $ modifyTVar (view downloads env) (HashMap.insert h whenDone) atomically $ modifyTVar (view downloads env) (HashMap.insert h whenDone)
data DownloadEnv e =
DownloadEnv
{ _blockInQ :: TVar (HashMap (Hash HbSync) BlockState)
, _blockInDirty :: TVar Bool
, _blockCheckQ :: TQueue (Hash HbSync)
, _blockSizeRecvQ :: TQueue (Peer e, Hash HbSync, Maybe Integer)
-- FIXME: trim!!
-- , _blockProposed :: Cache (Hash HbSync, Peer e) ()
, _downloadMon :: DownloadMonEnv
, _downloadBrains :: SomeBrains e
, _downloadProbe :: TVar AnyProbe
}
makeLenses 'DownloadEnv type DownloadConstr e m = ( MonadIO m
, EventEmitter e (DownloadReq e) m
downloadEnvSetProbe :: forall e m . (MonadIO m, MyPeer e)
=> DownloadEnv e
-> AnyProbe
-> m ()
downloadEnvSetProbe DownloadEnv{..} p = do
atomically $ writeTVar _downloadProbe p
newDownloadEnv :: (MonadIO m, MyPeer e, HasBrains e brains) => brains -> m (DownloadEnv e)
newDownloadEnv brains = liftIO do
DownloadEnv <$> newTVarIO mempty
<*> newTVarIO False
<*> newTQueueIO
<*> newTQueueIO
-- <*> Cache.newCache (Just (toTimeSpec (2 :: Timeout 'Seconds)))
<*> downloadMonNew
<*> pure (SomeBrains brains)
<*> newTVarIO (AnyProbe ())
newtype BlockDownloadM e m a =
BlockDownloadM { fromBlockDownloadM :: ReaderT (DownloadEnv e) m a }
deriving newtype ( Functor
, Applicative
, Monad
, MonadIO
, MonadUnliftIO
, MonadReader (DownloadEnv e)
, MonadTrans
)
withDownload :: (MyPeer e, HasPeerLocator e m, MonadIO m) => DownloadEnv e -> BlockDownloadM e m a -> m a
withDownload e m = runReaderT ( fromBlockDownloadM m ) e
isBlockHereCached :: forall e m . ( MyPeer e
, MonadIO m
, HasStorage m
)
=> Hash HbSync -> BlockDownloadM e m Bool
isBlockHereCached h = do
sto <- lift getStorage
liftIO $ hasBlock sto h <&> isJust
type DownloadConstr e m = ( MyPeer e
, MonadIO m
, HasPeerLocator e (BlockDownloadM e m)
, HasStorage m -- (BlockDownloadM e m)
) )
addDownload :: forall e m . ( DownloadConstr e m addDownload :: forall e m . ( DownloadConstr e m
) )
=> Maybe (Hash HbSync) => Maybe (Hash HbSync)
-> Hash HbSync -> Hash HbSync
-> BlockDownloadM e m () -> m ()
addDownload mbh h = do addDownload mbh h = do
emit @e DownloadReqKey (DownloadReqData h)
tinq <- asks (view blockInQ)
checkQ <- asks (view blockCheckQ)
dirty <- asks (view blockInDirty)
brains <- asks (view downloadBrains)
here <- isBlockHereCached h
if here then do
deleteBlockFromQ h
else do
newBlock <- BlockState
<$> liftIO getTimeCoarse
<*> pure Nothing
<*> liftIO (newTVarIO BlkNew)
claimBlockCameFrom @e brains mbh h
-- Cache.insert
liftIO $ atomically $ do
modifyTVar tinq $ HashMap.insert h newBlock
writeTQueue checkQ h
writeTVar dirty True
deleteBlockFromQ :: MonadIO m => Hash HbSync -> BlockDownloadM e m ()
deleteBlockFromQ h = do
inq <- asks (view blockInQ)
liftIO $ atomically $ modifyTVar' inq (HashMap.delete h)
failedDownload :: forall e m . ( MyPeer e
, MonadIO m
, HasPeer e
, HasPeerLocator e (BlockDownloadM e m)
, HasStorage m
)
=> Peer e
-> Hash HbSync
-> BlockDownloadM e m ()
failedDownload p h = do
trace $ "failedDownload" <+> pretty p <+> pretty h
addDownload mzero h
-- FIXME: brains-download-fail
type ForGossip e p m = type ForGossip e p m =
( MonadIO m ( MonadIO m
@ -527,3 +427,46 @@ authorized f req = do
when auth (f req) when auth (f req)
-- NOTE: this is an adapter for a ResponseM monad
-- because response is working in ResponseM monad (ha!)
-- So don't be confused with types
--
mkAdapter :: forall e m . ( m ~ PeerM e IO
, HasProtocol e (BlockChunks e)
, Hashable (SessionKey e (BlockChunks e))
, Sessions e (BlockChunks e) (ResponseM e m)
, Typeable (SessionKey e (BlockChunks e))
, EventEmitter e (BlockChunks e) m
, Pretty (Peer e)
)
=> m (BlockChunksI e (ResponseM e m ))
mkAdapter = do
storage <- getStorage
pure $
BlockChunksI
{ blkSize = liftIO . hasBlock storage
, blkChunk = \h o s -> liftIO (getChunk storage h o s)
, blkGetHash = \c -> find (DownloadSessionKey @e c) (view sBlockHash)
, blkAcceptChunk = \(c,p,h,n,bs) -> void $ runMaybeT $ do
let cKey = DownloadSessionKey (p,c)
dodo <- lift $ find cKey (view sBlockChunks)
unless (isJust dodo) $ do
debug $ "session lost for peer !" <+> pretty p
-- debug $ "FINDING-SESSION:" <+> pretty c <+> pretty n
-- debug $ "GOT SHIT" <+> pretty c <+> pretty n
se <- MaybeT $ find cKey id
let dwnld = view sBlockChunks se
let dwnld2 = view sBlockChunks2 se
-- debug $ "WRITE SHIT" <+> pretty c <+> pretty n
liftIO $ atomically do
writeTQueue dwnld (n, bs)
modifyTVar' dwnld2 (IntMap.insert (fromIntegral n) bs)
}

View File

@ -1,7 +1,6 @@
{-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-orphans #-}
{-# Language UndecidableInstances #-} {-# Language UndecidableInstances #-}
{-# Language AllowAmbiguousTypes #-} {-# Language AllowAmbiguousTypes #-}
{-# LANGUAGE ImplicitParams #-}
module RPC2 module RPC2
( module RPC2.Peer ( module RPC2.Peer
, module RPC2.RefLog , module RPC2.RefLog
@ -51,6 +50,7 @@ import RPC2.Mailbox()
import PeerTypes import PeerTypes
import PeerInfo import PeerInfo
import BlockDownloadNew
import Control.Monad.Trans.Maybe import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Cont import Control.Monad.Trans.Cont
@ -77,462 +77,6 @@ import Lens.Micro.Platform
import Streaming.Prelude qualified as S import Streaming.Prelude qualified as S
data DownloadError e =
DownloadStuckError HashRef (Peer e)
| StorageError
| UnknownPeerError (Peer e)
| InternalError Int
| PeerMissBlockError HashRef (Peer e)
| PeerBlockHashMismatch (Peer e)
| PeerRequestTimeout (Peer e)
| Incomplete HashRef
deriving stock (Generic,Typeable)
instance Pretty (Peer e) => Show (DownloadError e) where
show (DownloadStuckError h p) = show $ parens $ "DownloadStuck" <+> pretty h <+> pretty p
show (UnknownPeerError p) = show $ parens $ "UnknownPeerError" <+> pretty p
show (PeerMissBlockError h p) = show $ parens $ "PeerMissBlockError" <+> pretty h <+> pretty p
show (PeerRequestTimeout p) = show $ parens $ "PeerRequestTimeout" <+> pretty p
show (PeerBlockHashMismatch p) = show $ parens $ "PeerBlockHashMismatch" <+> pretty p
show StorageError = show "StorageError"
show (InternalError n) = show $ parens "InternalError" <+> pretty n
show (Incomplete h) = show $ parens "Incomplete" <+> pretty h
instance (Typeable e, Pretty (Peer e)) => Exception (DownloadError e)
class BlockSizeCache e cache where
cacheBlockSize :: forall m . MonadUnliftIO m
=> cache
-> PubKey 'Sign (Encryption e)
-> Hash HbSync
-> Integer
-> m ()
findBlockSize :: forall m . MonadUnliftIO m
=> cache
-> PubKey 'Sign (Encryption e)
-> Hash HbSync
-> m (Maybe Integer)
instance BlockSizeCache e () where
cacheBlockSize _ _ _ _ = pure ()
findBlockSize _ _ _ = pure Nothing
instance BlockSizeCache e (SomeBrains e) where
cacheBlockSize = brainsCacheBlockSize @e
findBlockSize = brainsFindBlockSize @e
queryBlockSizeFromPeer :: forall e cache m . ( e ~ L4Proto
, MonadUnliftIO m
, BlockSizeCache e cache
)
=> cache
-> PeerEnv e
-> Hash HbSync
-> Peer e
-> m (Either (DownloadError e) (Maybe Integer))
queryBlockSizeFromPeer cache e h peer = do
what <- try @_ @(DownloadError e) $ liftIO $ withPeerM e do
flip runContT pure $ callCC \exit -> do
PeerData{..} <- lift $ find (KnownPeerKey peer) id
>>= orThrow (UnknownPeerError peer)
s <- lift $ findBlockSize @e cache _peerSignKey h
debug $ "FOUND CACHED VALUE" <+> pretty h <+> pretty s
maybe none (exit . Just) s
lift do
sizeQ <- newTQueueIO
subscribe @e (BlockSizeEventKey peer) $ \case
BlockSizeEvent (that, hx, sz) | hx == h -> do
atomically $ writeTQueue sizeQ (Just sz)
cacheBlockSize @e cache _peerSignKey h sz
_ -> do
atomically $ writeTQueue sizeQ Nothing
request peer (GetBlockSize @e h)
race ( pause defBlockInfoTimeout ) (atomically $ readTQueue sizeQ )
>>= orThrow (PeerRequestTimeout peer)
case what of
Left{} -> pure $ Left (PeerRequestTimeout peer)
Right x -> pure (Right x)
data BurstMachine =
BurstMachine
{ _buTimeout :: Double
, _buBurstMax :: Int
, _buStepUp :: Double
, _buStepDown :: Double
, _buCurrent :: TVar Double
, _buErrors :: TVar Int
}
burstMachineAddErrors :: MonadUnliftIO m => BurstMachine -> Int -> m ()
burstMachineAddErrors BurstMachine{..} n =
atomically $ modifyTVar _buErrors (+n)
newBurstMachine :: MonadUnliftIO m
=> Double -- ^ timeout
-> Int -- ^ max burst
-> Maybe Int -- ^ start burst
-> Double -- ^ step up
-> Double -- ^ step down
-> m BurstMachine
newBurstMachine t0 buMax buStart up' down' = do
BurstMachine t0 buMax up down
<$> newTVarIO bu0
<*> newTVarIO 0
where
bu0 = realToFrac $ fromMaybe (max 2 (buMax `div` 2)) buStart
down = min 0.85 down'
up = min 0.5 up'
getCurrentBurst :: MonadUnliftIO m => BurstMachine -> m Int
getCurrentBurst BurstMachine{..} = readTVarIO _buCurrent <&> round
runBurstMachine :: MonadUnliftIO m
=> BurstMachine
-> m ()
runBurstMachine BurstMachine{..} = do
bu0 <- readTVarIO _buCurrent <&> realToFrac
let buMax = realToFrac _buBurstMax
let down = _buStepDown
let up = _buStepUp
_dEdT <- newTVarIO 0.00
_rates <- newTVarIO (mempty :: Map Double Double)
_buMaxReal <- newTVarIO buMax
pause @'Seconds (realToFrac _buTimeout)
flip runContT pure do
void $ ContT $ withAsync do
forever do
pause @'Seconds (realToFrac _buTimeout * 10)
atomically do
e <- headDef bu0 . Map.elems <$> readTVar _rates
nrates <- readTVar _rates <&> take 100 . Map.toList
writeTVar _rates (Map.fromList nrates)
modifyTVar _buMaxReal (max e)
void $ ContT $ withAsync do
forever do
pause @'Seconds 600
atomically $ writeTVar _buMaxReal buMax
void $ ContT $ withAsync do
forever do
pause @'Seconds (realToFrac _buTimeout * 2.0)
ddt <- readTVarIO _dEdT
when (ddt <= 0) do
atomically do
buMaxReal <- readTVar _buMaxReal
current <- readTVar _buCurrent
let new = min buMaxReal (current * (1.0 + up))
writeTVar _buCurrent new
flip fix 0 $ \next e1 -> do
let dt = realToFrac _buTimeout
eNew <- atomically do
e2 <- readTVar _buErrors
current <- readTVar _buCurrent
new <- if e2 > e1 then do
let d = max 2.0 (current * (1.0 - down))
nrates <- readTVar _rates <&> drop 3 . Map.toList
let newFucked = maybe d snd (headMay nrates)
writeTVar _rates (Map.fromList nrates)
pure newFucked
else
pure current -- $ min buMaxReal (current * (1.0 + up))
writeTVar _buErrors 0
writeTVar _buCurrent new
let dedt = realToFrac (e2 - e1) / realToFrac dt
writeTVar _dEdT (realToFrac dedt)
modifyTVar _rates ( Map.insertWith max dedt current )
pure e2
pause @'Seconds dt
next eNew
data S =
SInit
| SFetchQ
| SFetchPost (Hash HbSync) ByteString
| SCheckBefore
| SCheckAfter
-- | downloads block with dependencies recursively
downloadFromPeerRec :: forall e t cache m . ( e ~ L4Proto
, MonadUnliftIO m
, IsTimeout t
, BlockSizeCache e cache
)
=> Timeout t
-> Int
-> cache
-> PeerEnv e
-> Hash HbSync
-> Peer e
-> m (Either (DownloadError e) ())
downloadFromPeerRec t bu0 cache env h0 peer = do
sto <- withPeerM env getStorage
p <- newTQueueIO
q <- newTQueueIO
qq <- newTQueueIO
toq <- newTVarIO ( mempty :: [Int] )
bm <- newBurstMachine 0.5 256 (Just bu0) 0.05 0.10
flip runContT pure do
ContT $ withAsync $ forever do
join $ atomically (readTQueue p)
ContT $ withAsync $ forever do
h <- atomically (readTQueue qq)
void $ queryBlockSizeFromPeer cache env h peer
pause @'Seconds 1.5
ContT $ withAsync $ flip fix 10000000 $ \next m0 -> do
txs <- readTVarIO toq <&> L.take 1000
let m1 = fromMaybe m0 $ median txs
when ( m1 > m0 ) $ burstMachineAddErrors bm 1
pause @'Seconds 3
next m1
ContT $ withAsync $ runBurstMachine bm
flip fix SInit $ \next -> \case
SInit -> do
debug "SInit"
atomically $ writeTQueue q h0
next SCheckBefore
SCheckBefore -> do
here <- hasBlock sto h0 <&> isJust
if here then next SCheckAfter else next SFetchQ
SFetchQ -> do
debug "SFetchQ"
done <- atomically do
pe <- isEmptyTQueue p
qe <- isEmptyTQueue q
when (qe && not pe) retry
-- when (not pe) retry
pure qe
if done then
next SCheckAfter
else do
h <- atomically $ readTQueue q
mbs <- getBlock sto h
case mbs of
Just bs -> next (SFetchPost h bs)
Nothing -> none
bu <- lift $ getCurrentBurst bm
t0 <- getTimeCoarse
w <- lift $ downloadFromPeer t bu cache env (coerce h) peer
t1 <- getTimeCoarse
let dt = toMicroSeconds $ TimeoutTS (t1 - t0)
atomically $ modifyTVar toq ( dt : )
case w of
Right bs -> do
next (SFetchPost h bs)
Left e -> do
lift $ burstMachineAddErrors bm 1
err $ "DOWNLOAD ERROR" <+> viaShow e
next SFetchQ
SFetchPost h bs -> do
debug $ "SFetchPost" <+> pretty h
let parse = do
let refs = extractBlockRefs h bs
atomically $ mapM_ (writeTQueue q . coerce) refs
mapM_ (atomically . writeTQueue qq . coerce) refs
atomically $ writeTQueue p parse
next SFetchQ
SCheckAfter -> do
debug "SCheckAfter"
missed <- findMissedBlocks sto (HashRef h0)
mapM_ (atomically . writeTQueue q . coerce) missed
mapM_ (atomically . writeTQueue qq . coerce) missed
unless (L.null missed) $ next SFetchQ
pure $ Right ()
downloadFromPeer :: forall e t cache m . ( e ~ L4Proto
, MonadUnliftIO m
, IsTimeout t
, BlockSizeCache e cache
)
=> Timeout t
-> Int
-> cache
-> PeerEnv e
-> Hash HbSync
-> Peer e
-> m (Either (DownloadError e) ByteString)
downloadFromPeer t bu cache env h peer = liftIO $ withPeerM env do
pd@PeerData{..} <- find (KnownPeerKey peer) id
>>= orThrow (UnknownPeerError peer)
pinfo <- find (PeerInfoKey peer) id
>>= orThrow (UnknownPeerError peer)
rtt <- liftIO $ medianPeerRTT pinfo
<&> fmap ((*1) . realToFrac)
<&> fromMaybe 1000
<&> (/1e6)
let waity = 10 * rtt
sto <- getStorage
let chunkSize = defChunkSize
flip runContT pure $ callCC \exit -> do
size <- lift (findBlockSize @e cache _peerSignKey h)
>>= maybe (queryBlockSize exit) pure
coo <- genCookie (peer,h)
let key = DownloadSessionKey (peer, coo)
down@BlockDownload{..} <- newBlockDownload h
let chuQ = _sBlockChunks
let new = set sBlockChunkSize chunkSize
. set sBlockSize (fromIntegral size)
$ down
lift $ update @e new key id
let offsets = calcChunks size (fromIntegral chunkSize) :: [(Offset, Size)]
let chunkNums = [ 0 .. pred (length offsets) ]
let bursts = calcBursts bu chunkNums
callCC $ \exit2 -> do
_wx <- newTVarIO waity
for_ bursts $ \(i,chunkN) -> do
-- atomically $ flushTQueue chuQ
let req = BlockChunks @e coo (BlockGetChunks h chunkSize (fromIntegral i) (fromIntegral chunkN))
lift $ request peer req
t0 <- getTimeCoarse
let watchdog = fix \next -> do
wx <- readTVarIO _wx <&> realToFrac
-- debug $ "WATCHDOG" <+> pretty wx <+> pretty waity
r <- race (pause @'MilliSeconds (min wx waity)) do
void $ atomically $ readTQueue chuQ
either (const none) (const next) r
r <- liftIO $ race watchdog do
atomically do
pieces <- readTVar _sBlockChunks2
let done = and [ IntMap.member j pieces | j <- [i .. i + chunkN-1] ]
unless done retry
atomically $ flushTQueue chuQ
t1 <- getTimeCoarse
atomically do
wx0 <- readTVar _wx
let wx1 = realToFrac (t1 - t0) * 100 / 1e6 -- millis
writeTVar _wx wx1
case r of
Left{} -> exit2 (Left $ DownloadStuckError (HashRef h) peer)
_ -> pure ()
blk <- readTVarIO _sBlockChunks2
let rs = LBS.concat $ IntMap.elems blk
ha <- putBlock sto rs
-- let ha = Just $ hashObject @HbSync rs
lift $ expire @e key
case ha of
Nothing -> pure $ Left StorageError
Just h1 | h1 == h -> do
pure $ Right rs
Just h1 -> do
delBlock sto h1
pure $ Left (PeerBlockHashMismatch peer)
where
queryBlockSize exit = do
what <- lift $ queryBlockSizeFromPeer cache env h peer
case what of
Left{} -> exit (Left (PeerRequestTimeout peer))
Right Nothing -> exit (Left (PeerMissBlockError (HashRef h) peer))
Right (Just s) -> pure s
instance (e ~ L4Proto, MonadUnliftIO m, HasRpcContext PeerAPI RPC2Context m) => HandleMethod m RpcRunScript where instance (e ~ L4Proto, MonadUnliftIO m, HasRpcContext PeerAPI RPC2Context m) => HandleMethod m RpcRunScript where
handleMethod top = do handleMethod top = do

View File

@ -38,7 +38,6 @@ import HBS2.Storage
import PeerTypes hiding (downloads) import PeerTypes hiding (downloads)
import PeerConfig import PeerConfig
import BlockDownload()
import Brains import Brains
import Control.Monad.Trans.Cont import Control.Monad.Trans.Cont
@ -91,7 +90,6 @@ data RefChanWorkerEnv e =
RefChanWorkerEnv RefChanWorkerEnv
{ _refChanWorkerConf :: PeerConfig { _refChanWorkerConf :: PeerConfig
, _refChanPeerEnv :: PeerEnv e , _refChanPeerEnv :: PeerEnv e
, _refChanWorkerEnvDEnv :: DownloadEnv e
, _refChanNotifySource :: SomeNotifySource (RefChanEvents e) , _refChanNotifySource :: SomeNotifySource (RefChanEvents e)
, _refChanWorkerEnvHeadQ :: TQueue (RefChanId e, RefChanHeadBlockTran e) , _refChanWorkerEnvHeadQ :: TQueue (RefChanId e, RefChanHeadBlockTran e)
, _refChanWorkerEnvDownload :: TVar (HashMap HashRef (RefChanId e, (TimeSpec, OnDownloadComplete))) , _refChanWorkerEnvDownload :: TVar (HashMap HashRef (RefChanId e, (TimeSpec, OnDownloadComplete)))
@ -121,12 +119,11 @@ refChanWorkerEnvSetProbe RefChanWorkerEnv{..} probe = do
refChanWorkerEnv :: forall m e . (MonadIO m, ForRefChans e) refChanWorkerEnv :: forall m e . (MonadIO m, ForRefChans e)
=> PeerConfig => PeerConfig
-> PeerEnv e -> PeerEnv e
-> DownloadEnv e
-> SomeNotifySource (RefChanEvents e) -> SomeNotifySource (RefChanEvents e)
-> m (RefChanWorkerEnv e) -> m (RefChanWorkerEnv e)
refChanWorkerEnv conf pe de nsource = refChanWorkerEnv conf pe nsource =
liftIO $ RefChanWorkerEnv @e conf pe de nsource liftIO $ RefChanWorkerEnv @e conf pe nsource
<$> newTQueueIO <$> newTQueueIO
<*> newTVarIO mempty <*> newTVarIO mempty
<*> newTVarIO mempty <*> newTVarIO mempty
@ -217,8 +214,7 @@ refChanAddDownload :: forall e m . ( m ~ PeerM e IO
refChanAddDownload env chan r onComlete = do refChanAddDownload env chan r onComlete = do
penv <- ask penv <- ask
t <- getTimeCoarse t <- getTimeCoarse
withPeerM penv $ withDownload (_refChanWorkerEnvDEnv env) liftIO $ withPeerM penv $ addDownload @e Nothing (fromHashRef r)
$ addDownload @e Nothing (fromHashRef r)
atomically $ modifyTVar (view refChanWorkerEnvDownload env) (HashMap.insert r (chan,(t, onComlete))) atomically $ modifyTVar (view refChanWorkerEnvDownload env) (HashMap.insert r (chan,(t, onComlete)))
@ -903,7 +899,11 @@ logMergeProcess penv env q = withPeerM penv do
atomically $ modifyTVar (mergeHeads e) (HashMap.insert h headblk) atomically $ modifyTVar (mergeHeads e) (HashMap.insert h headblk)
pure headblk pure headblk
downloadMissedHead :: AnyStorage -> RefChanId e -> HashRef -> m () downloadMissedHead :: EventEmitter e (DownloadReq e) m
=> AnyStorage
-> RefChanId e
-> HashRef
-> m ()
downloadMissedHead sto chan headRef = do downloadMissedHead sto chan headRef = do
penv <- ask penv <- ask
here <- liftIO $ hasBlock sto (fromHashRef headRef) <&> isJust here <- liftIO $ hasBlock sto (fromHashRef headRef) <&> isJust

View File

@ -245,10 +245,9 @@ executable hbs2-peer
main-is: PeerMain.hs main-is: PeerMain.hs
other-modules: other-modules:
BlockDownload BlockDownloadNew
, BrainyPeerLocator , BrainyPeerLocator
, ByPassWorker , ByPassWorker
, DownloadQ
, DownloadMon , DownloadMon
, Bootstrap , Bootstrap
, PeerInfo , PeerInfo

View File

@ -10,6 +10,7 @@ import HBS2.Hash
import HBS2.System.Logger.Simple import HBS2.System.Logger.Simple
import Type.Reflection (someTypeRep)
import Data.Hashable import Data.Hashable
import Data.Maybe import Data.Maybe
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
@ -33,13 +34,12 @@ blockSizeProto :: forall e m proto . ( MonadIO m
, proto ~ BlockInfo e , proto ~ BlockInfo e
) )
=> GetBlockSize HbSync m => GetBlockSize HbSync m
-> HasBlockEvent HbSync e m
-> ( (Peer e, Hash HbSync) -> m () ) -> ( (Peer e, Hash HbSync) -> m () )
-> BlockInfo e -> BlockInfo e
-> m () -> m ()
-- FIXME: with-auth-combinator -- FIXME: with-auth-combinator
blockSizeProto getBlockSize evHasBlock onNoBlock = blockSizeProto getBlockSize onNoBlock =
\case \case
GetBlockSize h -> do GetBlockSize h -> do
-- liftIO $ print "GetBlockSize" -- liftIO $ print "GetBlockSize"
@ -57,13 +57,13 @@ blockSizeProto getBlockSize evHasBlock onNoBlock =
that <- thatPeer @proto that <- thatPeer @proto
emit @e (BlockSizeEventKey that) (NoBlockEvent (that, h)) emit @e (BlockSizeEventKey that) (NoBlockEvent (that, h))
emit @e AnyBlockSizeEventKey (AnyBlockSizeEvent h Nothing that) emit @e AnyBlockSizeEventKey (AnyBlockSizeEvent h Nothing that)
evHasBlock ( that, h, Nothing ) -- evHasBlock ( that, h, Nothing )
BlockSize h sz -> deferred @proto do BlockSize h sz -> deferred @proto do
that <- thatPeer @proto that <- thatPeer @proto
emit @e (BlockSizeEventKey @e that) (BlockSizeEvent (that, h, sz)) emit @e (BlockSizeEventKey @e that) (BlockSizeEvent (that, h, sz))
emit @e AnyBlockSizeEventKey (AnyBlockSizeEvent h (Just sz) that) emit @e AnyBlockSizeEventKey (AnyBlockSizeEvent h (Just sz) that)
evHasBlock ( that, h, Just sz ) -- evHasBlock ( that, h, Just sz )
data AnyBlockSizeEvent e data AnyBlockSizeEvent e
@ -71,8 +71,15 @@ data instance EventKey e (AnyBlockSizeEvent e) =
AnyBlockSizeEventKey AnyBlockSizeEventKey
deriving stock (Typeable, Generic, Eq) deriving stock (Typeable, Generic, Eq)
instance Hashable (EventKey e (AnyBlockSizeEvent e)) where instance Typeable e => Hashable (EventKey e (AnyBlockSizeEvent e)) where
hashWithSalt s _ = hashWithSalt s ("AnyBlockSizeEventKey_1730696922" :: ByteString) hashWithSalt s _ =
hashWithSalt s (someTypeRep (Proxy :: Proxy (EventKey e (AnyBlockSizeEvent e))) :: TypeRep)
instance EventType (Event e (AnyBlockSizeEvent e)) where
isPersistent = True
instance Expires (EventKey e (AnyBlockSizeEvent e)) where
expiresIn = const Nothing -- (Just defCookieTimeoutSec)
data instance Event e (AnyBlockSizeEvent e) = data instance Event e (AnyBlockSizeEvent e) =
AnyBlockSizeEvent AnyBlockSizeEvent