mirror of https://github.com/voidlizard/hbs2
better
This commit is contained in:
parent
fe27c56c35
commit
2d06149e25
|
@ -16,12 +16,17 @@ import HBS2.Actors
|
||||||
import HBS2.Hash
|
import HBS2.Hash
|
||||||
import HBS2.Storage
|
import HBS2.Storage
|
||||||
import HBS2.Defaults
|
import HBS2.Defaults
|
||||||
|
import HBS2.Clock
|
||||||
|
|
||||||
|
import Data.Functor
|
||||||
|
import Data.Function
|
||||||
import Control.Exception
|
import Control.Exception
|
||||||
import Data.ByteString.Lazy (ByteString)
|
import Data.ByteString.Lazy (ByteString)
|
||||||
import Data.ByteString.Lazy qualified as B
|
import Data.ByteString.Lazy qualified as B
|
||||||
-- import Data.Cache (Cache)
|
-- import Data.Cache (Cache)
|
||||||
-- import Data.Cache qualified as Cache
|
-- import Data.Cache qualified as Cache
|
||||||
|
import Data.Foldable
|
||||||
|
import Data.Traversable
|
||||||
import Data.Hashable (hash)
|
import Data.Hashable (hash)
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.Word
|
import Data.Word
|
||||||
|
@ -34,10 +39,18 @@ import System.IO.Temp
|
||||||
|
|
||||||
import Control.Concurrent.Async
|
import Control.Concurrent.Async
|
||||||
|
|
||||||
|
import Data.Cache (Cache)
|
||||||
|
import Data.Cache qualified as Cache
|
||||||
import Control.Concurrent.STM
|
import Control.Concurrent.STM
|
||||||
import Control.Concurrent.STM.TBQueue qualified as Q
|
import Control.Concurrent.STM.TBQueue qualified as Q
|
||||||
|
import Control.Concurrent.STM.TSem qualified as Sem
|
||||||
|
import Control.Concurrent.STM.TSem (TSem)
|
||||||
|
|
||||||
-- TODO: cache file handles
|
import Control.Concurrent.STM.TQueue qualified as Q0
|
||||||
|
import Control.Concurrent
|
||||||
|
--
|
||||||
|
--
|
||||||
|
--TODO: cache file handles
|
||||||
|
|
||||||
newtype ChunkId = ChunkId FilePath
|
newtype ChunkId = ChunkId FilePath
|
||||||
deriving newtype (IsString)
|
deriving newtype (IsString)
|
||||||
|
@ -48,20 +61,72 @@ data ChunkWriter h m = forall a . ( MonadIO m
|
||||||
, Block ByteString ~ ByteString
|
, Block ByteString ~ ByteString
|
||||||
) =>
|
) =>
|
||||||
ChunkWriter
|
ChunkWriter
|
||||||
{ pipeline :: Pipeline m ()
|
{ stopped :: TVar Bool
|
||||||
|
, pipeline :: Pipeline m ()
|
||||||
, dir :: FilePath
|
, dir :: FilePath
|
||||||
, storage :: a
|
, storage :: a
|
||||||
|
, perBlock :: Cache FilePath (TQueue (Handle -> IO ()))
|
||||||
|
, semFlush :: Cache FilePath TSem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
runChunkWriter :: forall h m . ( Eq (Hash h)
|
||||||
|
, Hashable (Hash h)
|
||||||
|
, MonadIO m )
|
||||||
|
=> ChunkWriter h m -> m ()
|
||||||
|
|
||||||
runChunkWriter :: MonadIO m => ChunkWriter h m -> m ()
|
runChunkWriter = runChunkWriter2
|
||||||
runChunkWriter w = do
|
|
||||||
|
runChunkWriter1 :: forall h m . ( Eq (Hash h)
|
||||||
|
, Hashable (Hash h)
|
||||||
|
, MonadIO m )
|
||||||
|
=> ChunkWriter h m -> m ()
|
||||||
|
|
||||||
|
runChunkWriter1 w = do
|
||||||
liftIO $ createDirectoryIfMissing True ( dir w )
|
liftIO $ createDirectoryIfMissing True ( dir w )
|
||||||
runPipeline (pipeline w)
|
runPipeline (pipeline w)
|
||||||
|
|
||||||
|
|
||||||
|
runChunkWriter2 :: forall h m . ( Eq (Hash h)
|
||||||
|
, Hashable (Hash h)
|
||||||
|
, MonadIO m )
|
||||||
|
=> ChunkWriter h m -> m ()
|
||||||
|
|
||||||
|
runChunkWriter2 w = do
|
||||||
|
liftIO $ createDirectoryIfMissing True ( dir w )
|
||||||
|
let cache = perBlock w
|
||||||
|
fix \next -> do
|
||||||
|
-- kks <- liftIO $ take 1 <$> Cache.keys cache
|
||||||
|
-- liftIO $ for_ kks $ \h -> flush w h
|
||||||
|
|
||||||
|
-- pause ( 1 :: Timeout 'Seconds )
|
||||||
|
-- yield
|
||||||
|
-- next
|
||||||
|
|
||||||
|
stop <- liftIO $ readTVarIO (stopped w)
|
||||||
|
|
||||||
|
if stop then do
|
||||||
|
ks <- liftIO $ take 20 <$> Cache.keys cache
|
||||||
|
for_ ks $ \k -> flush w k
|
||||||
|
else do
|
||||||
|
ks <- liftIO $ Cache.keys cache
|
||||||
|
|
||||||
|
amount <- for ks $ \k -> flush w k
|
||||||
|
|
||||||
|
if (sum amount == 0) then do
|
||||||
|
pause ( 0.5 :: Timeout 'Seconds )
|
||||||
|
else do
|
||||||
|
liftIO $ print ("flushed:" <+> pretty (sum amount))
|
||||||
|
|
||||||
|
|
||||||
stopChunkWriter :: MonadIO m => ChunkWriter h m -> m ()
|
stopChunkWriter :: MonadIO m => ChunkWriter h m -> m ()
|
||||||
stopChunkWriter w = stopPipeline ( pipeline w )
|
stopChunkWriter w = do
|
||||||
|
liftIO $ atomically $ writeTVar (stopped w) True
|
||||||
|
|
||||||
|
stopChunkWriter1 :: MonadIO m => ChunkWriter h m -> m ()
|
||||||
|
stopChunkWriter1 w = do
|
||||||
|
let cache = perBlock w
|
||||||
|
stopPipeline ( pipeline w )
|
||||||
|
|
||||||
newChunkWriterIO :: forall h a m . ( Key h ~ Hash h, h ~ HbSync
|
newChunkWriterIO :: forall h a m . ( Key h ~ Hash h, h ~ HbSync
|
||||||
, Storage a h ByteString m
|
, Storage a h ByteString m
|
||||||
|
@ -78,11 +143,19 @@ newChunkWriterIO s tmp = do
|
||||||
def <- liftIO $ getXdgDirectory XdgData (defStorePath </> "temp-chunks")
|
def <- liftIO $ getXdgDirectory XdgData (defStorePath </> "temp-chunks")
|
||||||
let d = fromMaybe def tmp
|
let d = fromMaybe def tmp
|
||||||
|
|
||||||
|
mt <- liftIO $ Cache.newCache Nothing
|
||||||
|
sem <- liftIO $ Cache.newCache Nothing
|
||||||
|
|
||||||
|
running <- liftIO $ newTVarIO False
|
||||||
|
|
||||||
pure $
|
pure $
|
||||||
ChunkWriter
|
ChunkWriter
|
||||||
{ pipeline = pip
|
{ stopped = running
|
||||||
|
, pipeline = pip
|
||||||
, dir = d
|
, dir = d
|
||||||
, storage = s
|
, storage = s
|
||||||
|
, perBlock = mt
|
||||||
|
, semFlush = sem
|
||||||
}
|
}
|
||||||
|
|
||||||
makeFileName :: (Hashable salt, Pretty (Hash h)) => ChunkWriter h m -> salt -> Hash h -> FilePath
|
makeFileName :: (Hashable salt, Pretty (Hash h)) => ChunkWriter h m -> salt -> Hash h -> FilePath
|
||||||
|
@ -113,14 +186,63 @@ delBlock w salt h = liftIO do
|
||||||
where
|
where
|
||||||
fn = makeFileName w salt h
|
fn = makeFileName w salt h
|
||||||
|
|
||||||
writeChunk :: (Hashable salt, MonadIO m, Pretty (Hash h))
|
writeChunk :: ( Hashable salt
|
||||||
|
, MonadIO m
|
||||||
|
, Pretty (Hash h)
|
||||||
|
, Hashable (Hash h), Eq (Hash h)
|
||||||
|
)
|
||||||
=> ChunkWriter h m
|
=> ChunkWriter h m
|
||||||
-> salt
|
-> salt
|
||||||
-> Hash h
|
-> Hash h
|
||||||
-> Offset
|
-> Offset
|
||||||
-> ByteString -> m ()
|
-> ByteString -> m ()
|
||||||
|
|
||||||
writeChunk w salt h o bs = addJob (pipeline w) $ liftIO do
|
writeChunk = writeChunk2
|
||||||
|
|
||||||
|
|
||||||
|
getHash :: forall salt h m .
|
||||||
|
( Hashable salt
|
||||||
|
, Hashed h ByteString
|
||||||
|
, MonadIO m
|
||||||
|
, Block ByteString ~ ByteString
|
||||||
|
, Pretty (Hash h)
|
||||||
|
, Hashable (Hash h), Eq (Hash h)
|
||||||
|
)
|
||||||
|
=> ChunkWriter h m
|
||||||
|
-> salt
|
||||||
|
-> Hash h
|
||||||
|
-> m (Hash h)
|
||||||
|
|
||||||
|
getHash = getHash2
|
||||||
|
|
||||||
|
|
||||||
|
commitBlock :: forall salt h m .
|
||||||
|
( Hashable salt
|
||||||
|
, Hashed h ByteString
|
||||||
|
, Block ByteString ~ ByteString
|
||||||
|
, MonadIO m
|
||||||
|
, Pretty (Hash h)
|
||||||
|
, Hashable (Hash h), Eq (Hash h)
|
||||||
|
)
|
||||||
|
=> ChunkWriter h m
|
||||||
|
-> salt
|
||||||
|
-> Hash h
|
||||||
|
-> m ()
|
||||||
|
|
||||||
|
commitBlock = commitBlock2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
writeChunk1 :: (Hashable salt, MonadIO m, Pretty (Hash h))
|
||||||
|
=> ChunkWriter h m
|
||||||
|
-> salt
|
||||||
|
-> Hash h
|
||||||
|
-> Offset
|
||||||
|
-> ByteString -> m ()
|
||||||
|
|
||||||
|
writeChunk1 w salt h o bs = addJob (pipeline w) $ liftIO do
|
||||||
|
-- writeChunk w salt h o bs = liftIO do
|
||||||
|
-- print $ "writeChunk:" <+> pretty fn
|
||||||
withBinaryFile fn ReadWriteMode $ \fh -> do
|
withBinaryFile fn ReadWriteMode $ \fh -> do
|
||||||
hSeek fh AbsoluteSeek (fromIntegral o)
|
hSeek fh AbsoluteSeek (fromIntegral o)
|
||||||
B.hPutStr fh bs
|
B.hPutStr fh bs
|
||||||
|
@ -129,23 +251,47 @@ writeChunk w salt h o bs = addJob (pipeline w) $ liftIO do
|
||||||
where
|
where
|
||||||
fn = makeFileName w salt h
|
fn = makeFileName w salt h
|
||||||
|
|
||||||
|
writeChunk2 :: (Hashable salt, MonadIO m, Pretty (Hash h), Hashable (Hash h), Eq (Hash h))
|
||||||
|
=> ChunkWriter h m
|
||||||
|
-> salt
|
||||||
|
-> Hash h
|
||||||
|
-> Offset
|
||||||
|
-> ByteString -> m ()
|
||||||
|
|
||||||
|
writeChunk2 w salt h o bs = do
|
||||||
|
|
||||||
|
let cache = perBlock w
|
||||||
|
|
||||||
|
liftIO $ do
|
||||||
|
q <- Cache.fetchWithCache cache fn $ const Q0.newTQueueIO
|
||||||
|
atomically $ Q0.writeTQueue q $ \fh -> do
|
||||||
|
-- withBinaryFile fn ReadWriteMode $ \fh -> do
|
||||||
|
hSeek fh AbsoluteSeek (fromIntegral o)
|
||||||
|
B.hPutStr fh bs
|
||||||
|
-- hFlush fh
|
||||||
|
|
||||||
|
where
|
||||||
|
fn = makeFileName w salt h
|
||||||
|
|
||||||
|
|
||||||
-- Blocking!
|
-- Blocking!
|
||||||
-- we need to write last chunk before this will happen
|
-- we need to write last chunk before this will happen
|
||||||
-- FIXME: incremental calculation,
|
-- FIXME: incremental calculation,
|
||||||
-- streaming, blah-blah
|
-- streaming, blah-blah
|
||||||
getHash :: forall salt h m .
|
getHash1 :: forall salt h m .
|
||||||
( Hashable salt
|
( Hashable salt
|
||||||
, Hashed h ByteString
|
, Hashed h ByteString
|
||||||
, MonadIO m
|
, MonadIO m
|
||||||
, Block ByteString ~ ByteString
|
, Block ByteString ~ ByteString
|
||||||
, Pretty (Hash h)
|
, Pretty (Hash h)
|
||||||
|
, Hashable (Hash h), Eq (Hash h)
|
||||||
)
|
)
|
||||||
=> ChunkWriter h m
|
=> ChunkWriter h m
|
||||||
-> salt
|
-> salt
|
||||||
-> Hash h
|
-> Hash h
|
||||||
-> m (Hash h)
|
-> m (Hash h)
|
||||||
|
|
||||||
getHash w salt h = liftIO do
|
getHash1 w salt h = liftIO do
|
||||||
|
|
||||||
q <- Q.newTBQueueIO 1
|
q <- Q.newTBQueueIO 1
|
||||||
|
|
||||||
|
@ -159,7 +305,57 @@ getHash w salt h = liftIO do
|
||||||
fn = makeFileName w salt h
|
fn = makeFileName w salt h
|
||||||
|
|
||||||
|
|
||||||
commitBlock :: forall salt h m .
|
flush w fn = do
|
||||||
|
let cache = perBlock w
|
||||||
|
let scache = semFlush w
|
||||||
|
liftIO $ do
|
||||||
|
q <- Cache.fetchWithCache cache fn $ const Q0.newTQueueIO
|
||||||
|
s <- Cache.fetchWithCache scache fn $ const (atomically $ Sem.newTSem 2)
|
||||||
|
|
||||||
|
atomically $ Sem.waitTSem s
|
||||||
|
|
||||||
|
Cache.delete cache fn
|
||||||
|
|
||||||
|
flushed <- atomically (Q0.flushTQueue q)
|
||||||
|
|
||||||
|
liftIO $ do
|
||||||
|
|
||||||
|
-- withBinaryFile fn ReadWriteMode $ \fh -> do
|
||||||
|
withFile fn ReadWriteMode $ \fh -> do
|
||||||
|
for_ flushed $ \f -> f fh
|
||||||
|
|
||||||
|
atomically $ Sem.signalTSem s
|
||||||
|
|
||||||
|
pure (length flushed)
|
||||||
|
|
||||||
|
|
||||||
|
-- Blocking!
|
||||||
|
-- we need to write last chunk before this will happen
|
||||||
|
-- FIXME: incremental calculation,
|
||||||
|
-- streaming, blah-blah
|
||||||
|
getHash2 :: forall salt h m .
|
||||||
|
( Hashable salt
|
||||||
|
, Hashed h ByteString
|
||||||
|
, MonadIO m
|
||||||
|
, Block ByteString ~ ByteString
|
||||||
|
, Pretty (Hash h)
|
||||||
|
, Hashable (Hash h), Eq (Hash h)
|
||||||
|
)
|
||||||
|
=> ChunkWriter h m
|
||||||
|
-> salt
|
||||||
|
-> Hash h
|
||||||
|
-> m (Hash h)
|
||||||
|
|
||||||
|
getHash2 w salt h = do
|
||||||
|
flush w fn
|
||||||
|
h1 <- liftIO $ hashObject @h <$> B.readFile fn
|
||||||
|
pure h1
|
||||||
|
|
||||||
|
where
|
||||||
|
fn = makeFileName w salt h
|
||||||
|
|
||||||
|
|
||||||
|
commitBlock1 :: forall salt h m .
|
||||||
( Hashable salt
|
( Hashable salt
|
||||||
, Hashed h ByteString
|
, Hashed h ByteString
|
||||||
, Block ByteString ~ ByteString
|
, Block ByteString ~ ByteString
|
||||||
|
@ -171,7 +367,7 @@ commitBlock :: forall salt h m .
|
||||||
-> Hash h
|
-> Hash h
|
||||||
-> m ()
|
-> m ()
|
||||||
|
|
||||||
commitBlock w@(ChunkWriter {storage = stor}) salt h = do
|
commitBlock1 w@(ChunkWriter {storage = stor}) salt h = do
|
||||||
q <- liftIO $ Q.newTBQueueIO 1
|
q <- liftIO $ Q.newTBQueueIO 1
|
||||||
|
|
||||||
addJob (pipeline w) (liftIO $ B.readFile fn >>= atomically . Q.writeTBQueue q)
|
addJob (pipeline w) (liftIO $ B.readFile fn >>= atomically . Q.writeTBQueue q)
|
||||||
|
@ -186,3 +382,31 @@ commitBlock w@(ChunkWriter {storage = stor}) salt h = do
|
||||||
fn = makeFileName w salt h
|
fn = makeFileName w salt h
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
commitBlock2 :: forall salt h m .
|
||||||
|
( Hashable salt
|
||||||
|
, Hashed h ByteString
|
||||||
|
, Block ByteString ~ ByteString
|
||||||
|
, MonadIO m
|
||||||
|
, Pretty (Hash h)
|
||||||
|
, Hashable (Hash h), Eq (Hash h)
|
||||||
|
)
|
||||||
|
=> ChunkWriter h m
|
||||||
|
-> salt
|
||||||
|
-> Hash h
|
||||||
|
-> m ()
|
||||||
|
|
||||||
|
commitBlock2 w@(ChunkWriter {storage = stor}) salt h = do
|
||||||
|
let cache = perBlock w
|
||||||
|
let scache = semFlush w
|
||||||
|
flush w fn
|
||||||
|
s <- liftIO $ B.readFile fn
|
||||||
|
void $ putBlock stor s
|
||||||
|
delBlock w salt h
|
||||||
|
liftIO $ Cache.delete cache fn
|
||||||
|
liftIO $ Cache.delete scache fn
|
||||||
|
|
||||||
|
where
|
||||||
|
fn = makeFileName w salt h
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,7 @@ runTestPeer p zu = do
|
||||||
cww <- newChunkWriterIO stor (Just chDir)
|
cww <- newChunkWriterIO stor (Just chDir)
|
||||||
|
|
||||||
sw <- liftIO $ replicateM 8 $ async $ simpleStorageWorker stor
|
sw <- liftIO $ replicateM 8 $ async $ simpleStorageWorker stor
|
||||||
cw <- liftIO $ replicateM 1 $ async $ runChunkWriter cww
|
cw <- liftIO $ replicateM 16 $ async $ runChunkWriter cww
|
||||||
|
|
||||||
zu stor cww
|
zu stor cww
|
||||||
|
|
||||||
|
@ -260,7 +260,7 @@ blockDownloadLoop = do
|
||||||
|
|
||||||
env <- ask
|
env <- ask
|
||||||
pip <- asks (view envDeferred)
|
pip <- asks (view envDeferred)
|
||||||
debug "process block!"
|
-- debug "process block!"
|
||||||
liftIO $ addJob pip $ withPeerM env $ do
|
liftIO $ addJob pip $ withPeerM env $ do
|
||||||
-- void $ liftIO $ async $ withPeerM env $ do
|
-- void $ liftIO $ async $ withPeerM env $ do
|
||||||
|
|
||||||
|
@ -353,6 +353,7 @@ mkAdapter cww = do
|
||||||
when ( h1 == h ) $ do
|
when ( h1 == h ) $ do
|
||||||
liftIO $ commitBlock cww cKey h
|
liftIO $ commitBlock cww cKey h
|
||||||
expire cKey
|
expire cKey
|
||||||
|
-- debug "hash matched!"
|
||||||
emit @e (BlockChunksEventKey h) (BlockReady h)
|
emit @e (BlockChunksEventKey h) (BlockReady h)
|
||||||
|
|
||||||
when (written > mbSize * defBlockDownloadThreshold) $ do
|
when (written > mbSize * defBlockDownloadThreshold) $ do
|
||||||
|
|
Loading…
Reference in New Issue