mirror of https://github.com/voidlizard/hbs2
wip
This commit is contained in:
parent
d8c34e3585
commit
617ce7d4db
|
@ -57,7 +57,9 @@ common shared-properties
|
||||||
|
|
||||||
library
|
library
|
||||||
import: shared-properties
|
import: shared-properties
|
||||||
exposed-modules: HBS2.Storage.NCQ
|
exposed-modules:
|
||||||
|
HBS2.Storage.NCQ
|
||||||
|
HBS2.Storage.NCQ2
|
||||||
-- other-modules:
|
-- other-modules:
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
build-depends: base, hbs2-core, hbs2-log-structured, suckless-conf
|
build-depends: base, hbs2-core, hbs2-log-structured, suckless-conf
|
||||||
|
|
|
@ -0,0 +1,221 @@
|
||||||
|
{-# Language MultiWayIf #-}
|
||||||
|
{-# Language RecordWildCards #-}
|
||||||
|
module HBS2.Storage.NCQ2 where
|
||||||
|
|
||||||
|
import HBS2.Prelude.Plated
|
||||||
|
import HBS2.Hash
|
||||||
|
import HBS2.OrDie
|
||||||
|
import HBS2.Data.Types.Refs
|
||||||
|
import HBS2.Base58
|
||||||
|
import HBS2.Net.Auth.Credentials
|
||||||
|
import HBS2.Storage
|
||||||
|
import HBS2.Misc.PrettyStuff
|
||||||
|
import HBS2.System.Logger.Simple.ANSI
|
||||||
|
|
||||||
|
import HBS2.Data.Log.Structured.NCQ
|
||||||
|
import HBS2.Data.Log.Structured.SD
|
||||||
|
|
||||||
|
import Data.Config.Suckless.System
|
||||||
|
import Data.Config.Suckless.Script hiding (void)
|
||||||
|
|
||||||
|
import Codec.Compression.Zstd qualified as Zstd
|
||||||
|
import Codec.Compression.Zstd.Lazy as ZstdL
|
||||||
|
import Codec.Compression.Zstd.Streaming qualified as ZstdS
|
||||||
|
import Codec.Compression.Zstd.Streaming (Result(..))
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
import Data.ByteString.Builder
|
||||||
|
import Network.ByteOrder qualified as N
|
||||||
|
import Data.HashMap.Strict (HashMap)
|
||||||
|
import Control.Monad.Except
|
||||||
|
import Control.Monad.Trans.Cont
|
||||||
|
import Control.Monad.Trans.Maybe
|
||||||
|
import Data.Ord (Down(..),comparing)
|
||||||
|
import Control.Concurrent.STM qualified as STM
|
||||||
|
import Data.HashPSQ qualified as HPSQ
|
||||||
|
import Data.HashPSQ (HashPSQ)
|
||||||
|
import Data.IntMap qualified as IntMap
|
||||||
|
import Data.IntMap (IntMap)
|
||||||
|
import Data.IntSet qualified as IntSet
|
||||||
|
import Data.IntSet (IntSet)
|
||||||
|
import Data.Sequence qualified as Seq
|
||||||
|
import Data.Sequence (Seq(..), (|>))
|
||||||
|
import Data.List qualified as List
|
||||||
|
import Data.ByteString.Lazy qualified as LBS
|
||||||
|
import Data.ByteString.Lazy.Char8 qualified as LBS8
|
||||||
|
import Data.ByteString (ByteString)
|
||||||
|
import Data.ByteString qualified as BS
|
||||||
|
import Data.ByteString.Char8 qualified as BS8
|
||||||
|
import Data.Char (isDigit)
|
||||||
|
import Data.Fixed
|
||||||
|
import Data.Coerce
|
||||||
|
import Data.Word
|
||||||
|
import Data.Either
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Text qualified as Text
|
||||||
|
import Data.Text.IO qualified as Text
|
||||||
|
import Data.Int
|
||||||
|
import Data.Vector qualified as V
|
||||||
|
import Data.Vector (Vector, (!))
|
||||||
|
import Lens.Micro.Platform
|
||||||
|
import Data.HashSet (HashSet)
|
||||||
|
import Data.HashSet qualified as HS
|
||||||
|
import Data.HashMap.Strict qualified as HM
|
||||||
|
import System.Directory (makeAbsolute)
|
||||||
|
import System.FilePath.Posix
|
||||||
|
import System.Posix.Fcntl
|
||||||
|
import System.Posix.Files qualified as Posix
|
||||||
|
import System.Posix.IO as PosixBase
|
||||||
|
import System.Posix.Types as Posix
|
||||||
|
import System.Posix.IO.ByteString as Posix
|
||||||
|
import System.Posix.Unistd
|
||||||
|
import System.Posix.Files ( getFileStatus
|
||||||
|
, modificationTimeHiRes
|
||||||
|
, setFileTimesHiRes
|
||||||
|
, getFdStatus
|
||||||
|
, FileStatus(..)
|
||||||
|
, setFileMode
|
||||||
|
)
|
||||||
|
import System.Posix.Files qualified as PFS
|
||||||
|
import System.IO.Error (catchIOError)
|
||||||
|
import System.IO.MMap as MMap
|
||||||
|
import System.IO.Temp (emptyTempFile)
|
||||||
|
import System.Mem
|
||||||
|
-- import Foreign.Ptr
|
||||||
|
-- import Foreign di
|
||||||
|
import qualified Data.ByteString.Internal as BSI
|
||||||
|
import Streaming.Prelude qualified as S
|
||||||
|
|
||||||
|
import UnliftIO
|
||||||
|
import UnliftIO.Concurrent(getNumCapabilities)
|
||||||
|
import UnliftIO.IO.File
|
||||||
|
|
||||||
|
import System.FileLock as FL
|
||||||
|
|
||||||
|
|
||||||
|
data NCQEntry =
|
||||||
|
NCQEntryNew ByteString
|
||||||
|
| NCQEntryJustWritten Handle Word64
|
||||||
|
|
||||||
|
type Shard = TVar (HashMap HashRef (TVar NCQEntry))
|
||||||
|
|
||||||
|
data NCQStorage2 =
|
||||||
|
NCQStorage2
|
||||||
|
{ ncqFsync :: Int
|
||||||
|
, ncqMemTable :: Vector Shard
|
||||||
|
, ncqWriteQ :: TBQueue HashRef
|
||||||
|
, ncqStorageStopReq :: TVar Bool
|
||||||
|
} deriving (Generic)
|
||||||
|
|
||||||
|
ncqStorageOpen2 :: MonadIO m => FilePath -> (NCQStorage2 -> NCQStorage2)-> m NCQStorage2
|
||||||
|
ncqStorageOpen2 fp upd = do
|
||||||
|
let ncqFsync = 16 * 1024^2
|
||||||
|
cap <- getNumCapabilities <&> fromIntegral
|
||||||
|
ncqWriteQ <- newTBQueueIO 32768
|
||||||
|
ncqMemTable <- V.fromList <$> replicateM cap (newTVarIO mempty)
|
||||||
|
ncqStorageStopReq <- newTVarIO False
|
||||||
|
pure $ NCQStorage2{..} & upd
|
||||||
|
|
||||||
|
ncqStorageStop2 :: MonadUnliftIO m => NCQStorage2 -> m ()
|
||||||
|
ncqStorageStop2 NCQStorage2{..} = do
|
||||||
|
atomically $ writeTVar ncqStorageStopReq True
|
||||||
|
|
||||||
|
ncqShardIdx :: NCQStorage2 -> HashRef -> Int
|
||||||
|
ncqShardIdx NCQStorage2{..} h =
|
||||||
|
fromIntegral (BS.head (coerce h)) `mod` V.length ncqMemTable
|
||||||
|
|
||||||
|
ncqGetShard :: NCQStorage2 -> HashRef -> Shard
|
||||||
|
ncqGetShard ncq@NCQStorage2{..} h = ncqMemTable ! ncqShardIdx ncq h
|
||||||
|
|
||||||
|
ncqLookupEntrySTM :: NCQStorage2 -> HashRef -> STM (Maybe (NCQEntry, TVar NCQEntry))
|
||||||
|
ncqLookupEntrySTM ncq h = do
|
||||||
|
readTVar (ncqGetShard ncq h)
|
||||||
|
<&> HM.lookup h
|
||||||
|
>>= \case
|
||||||
|
Nothing -> pure Nothing
|
||||||
|
Just tv -> do
|
||||||
|
v <- readTVar tv
|
||||||
|
pure $ Just (v, tv)
|
||||||
|
|
||||||
|
ncqPutBS :: MonadUnliftIO m => NCQStorage2 -> ByteString -> m HashRef
|
||||||
|
ncqPutBS ncq@NCQStorage2{..} bs = do
|
||||||
|
let h = HashRef (hashObject @HbSync bs)
|
||||||
|
atomically do
|
||||||
|
ncqAlterEntrySTM ncq h $ \case
|
||||||
|
Just e -> Just e
|
||||||
|
Nothing -> do
|
||||||
|
Just (NCQEntryNew bs)
|
||||||
|
writeTBQueue ncqWriteQ h
|
||||||
|
|
||||||
|
pure h
|
||||||
|
|
||||||
|
ncqLookupEntry :: MonadUnliftIO m => NCQStorage2 -> HashRef -> m (Maybe NCQEntry)
|
||||||
|
ncqLookupEntry sto hash = atomically (ncqLookupEntrySTM sto hash) <&> fmap fst
|
||||||
|
|
||||||
|
ncqAlterEntrySTM :: NCQStorage2 -> HashRef -> (Maybe NCQEntry -> Maybe NCQEntry) -> STM ()
|
||||||
|
ncqAlterEntrySTM ncq h alterFn = do
|
||||||
|
let shard = ncqGetShard ncq h
|
||||||
|
readTVar shard <&> HM.lookup h >>= \case
|
||||||
|
Just tve -> do
|
||||||
|
e <- readTVar tve
|
||||||
|
case alterFn (Just e) of
|
||||||
|
Nothing -> modifyTVar' shard (HM.delete h)
|
||||||
|
Just e' -> writeTVar tve e'
|
||||||
|
|
||||||
|
Nothing -> case alterFn Nothing of
|
||||||
|
Nothing -> modifyTVar' shard (HM.delete h)
|
||||||
|
Just e -> do
|
||||||
|
tve <- newTVar e
|
||||||
|
modifyTVar' shard (HM.insert h tve)
|
||||||
|
|
||||||
|
ncqStorageRun2 :: forall m . MonadUnliftIO m => NCQStorage2 -> m ()
|
||||||
|
ncqStorageRun2 ncq@NCQStorage2{..} = flip runContT pure do
|
||||||
|
|
||||||
|
fname <- liftIO $ emptyTempFile "." "datafile-.data"
|
||||||
|
|
||||||
|
let flags = defaultFileFlags { exclusive = False, creat = Just 0o666 }
|
||||||
|
fh0 <- liftIO (PosixBase.openFd fname Posix.ReadWrite flags)
|
||||||
|
|
||||||
|
ContT $ bracket none $ const do
|
||||||
|
liftIO $ closeFd fh0
|
||||||
|
|
||||||
|
flip fix (fh0,0) \loop (fh,w) -> do
|
||||||
|
|
||||||
|
what <- atomically do
|
||||||
|
h <- tryReadTBQueue ncqWriteQ
|
||||||
|
stop <- readTVar ncqStorageStopReq
|
||||||
|
|
||||||
|
case (stop,h) of
|
||||||
|
(False, Nothing) -> STM.retry
|
||||||
|
(True, Nothing) -> pure $ Left ()
|
||||||
|
(_, Just h) -> ncqLookupEntrySTM ncq h >>= \case
|
||||||
|
Nothing -> pure $ Right Nothing
|
||||||
|
Just (r,t) -> pure $ Right (Just (h,r,t))
|
||||||
|
|
||||||
|
case what of
|
||||||
|
Left _ -> exit
|
||||||
|
Right Nothing -> loop (fh,w)
|
||||||
|
Right (Just (h,r,t)) -> do
|
||||||
|
n <- lift (appendEntry fh h r)
|
||||||
|
|
||||||
|
w' <- if (w + n) < ncqFsync then do
|
||||||
|
pure (w + n)
|
||||||
|
else do
|
||||||
|
liftIO $ fileSynchronise fh
|
||||||
|
pure 0
|
||||||
|
|
||||||
|
loop (fh, w')
|
||||||
|
|
||||||
|
where
|
||||||
|
exit = none
|
||||||
|
|
||||||
|
appendEntry :: Fd -> HashRef -> NCQEntry -> m Int
|
||||||
|
|
||||||
|
appendEntry fh h (NCQEntryNew bs) = do
|
||||||
|
let ss = N.bytestring32 (32 + fromIntegral (BS.length bs))
|
||||||
|
let section = ss <> coerce h <> bs
|
||||||
|
liftIO (Posix.fdWrite fh section) <&> fromIntegral
|
||||||
|
|
||||||
|
appendEntry fh h _ = do
|
||||||
|
pure 0
|
||||||
|
|
|
@ -21,6 +21,7 @@ import HBS2.Storage.Operations.ByteString
|
||||||
import HBS2.System.Logger.Simple.ANSI
|
import HBS2.System.Logger.Simple.ANSI
|
||||||
|
|
||||||
import HBS2.Storage.NCQ
|
import HBS2.Storage.NCQ
|
||||||
|
import HBS2.Storage.NCQ2
|
||||||
import HBS2.Data.Log.Structured.NCQ
|
import HBS2.Data.Log.Structured.NCQ
|
||||||
|
|
||||||
import HBS2.CLI.Run.Internal.Merkle
|
import HBS2.CLI.Run.Internal.Merkle
|
||||||
|
@ -578,6 +579,64 @@ testNCQConcurrent1 noRead tn n TestEnv{..} = flip runContT pure do
|
||||||
|
|
||||||
rm ncqDir
|
rm ncqDir
|
||||||
|
|
||||||
|
|
||||||
|
testNCQ2Concurrent1 :: MonadUnliftIO m
|
||||||
|
=> Bool
|
||||||
|
-> Int
|
||||||
|
-> Int
|
||||||
|
-> TestEnv
|
||||||
|
-> m ()
|
||||||
|
|
||||||
|
testNCQ2Concurrent1 noRead tn n TestEnv{..} = flip runContT pure do
|
||||||
|
|
||||||
|
let tmp = testEnvDir
|
||||||
|
let inputDir = tmp </> "input"
|
||||||
|
let ncqDir = tmp </> "ncq-test-data"
|
||||||
|
|
||||||
|
debug "preparing"
|
||||||
|
|
||||||
|
mkdir inputDir
|
||||||
|
|
||||||
|
debug $ pretty inputDir
|
||||||
|
|
||||||
|
filez <- liftIO $ pooledReplicateConcurrentlyN 8 n $ do
|
||||||
|
size <- randomRIO (64*1024, 256*1024)
|
||||||
|
w <- liftIO (randomIO :: IO Word8)
|
||||||
|
let tbs = BS.replicate size w -- replicateM size w <&> BS.pack
|
||||||
|
let ha = hashObject @HbSync tbs -- & show . pretty
|
||||||
|
let fn = inputDir </> show (pretty ha)
|
||||||
|
liftIO $ BS.writeFile fn tbs
|
||||||
|
pure (fn, ha, BS.length tbs)
|
||||||
|
|
||||||
|
debug "done"
|
||||||
|
|
||||||
|
let fnv = V.fromList filez
|
||||||
|
let ssz = sum [ s | (_,_,s) <- filez ] & realToFrac
|
||||||
|
|
||||||
|
notice "NO SHIT"
|
||||||
|
|
||||||
|
setLoggingOff @DEBUG
|
||||||
|
|
||||||
|
for_ [1 .. tn] $ \tnn -> do
|
||||||
|
|
||||||
|
ncq1 <- ncqStorageOpen2 ncqDir (\x -> x { ncqFsync = 64^(1024^2) } )
|
||||||
|
w <- ContT $ withAsync (ncqStorageRun2 ncq1)
|
||||||
|
|
||||||
|
(t,_) <- timeItT $ liftIO do
|
||||||
|
|
||||||
|
pooledForConcurrentlyN_ tnn fnv $ \(n,ha,_) -> do
|
||||||
|
co <- BS.readFile n
|
||||||
|
ncqPutBS ncq1 co
|
||||||
|
|
||||||
|
let tt = realToFrac @_ @(Fixed E2) t
|
||||||
|
let speed = ((ssz / (1024 **2)) / t) & realToFrac @_ @(Fixed E2)
|
||||||
|
notice $ pretty tnn <+> pretty tt <+> pretty speed
|
||||||
|
|
||||||
|
rm ncqDir
|
||||||
|
|
||||||
|
lift $ ncqStorageStop2 ncq1
|
||||||
|
wait w
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
|
|
||||||
|
@ -680,6 +739,13 @@ main = do
|
||||||
|
|
||||||
e -> throwIO $ BadFormException @C (mkList e)
|
e -> throwIO $ BadFormException @C (mkList e)
|
||||||
|
|
||||||
|
entry $ bindMatch "test:ncq2:concurrent1" $ nil_ $ \case
|
||||||
|
[ LitIntVal tn, LitIntVal n ] -> do
|
||||||
|
debug $ "ncq2:concurrent1" <+> pretty tn <+> pretty n
|
||||||
|
runTest $ testNCQ2Concurrent1 False ( fromIntegral tn) (fromIntegral n)
|
||||||
|
|
||||||
|
e -> throwIO $ BadFormException @C (mkList e)
|
||||||
|
|
||||||
entry $ bindMatch "test:ncq:concurrent1:wo" $ nil_ $ \case
|
entry $ bindMatch "test:ncq:concurrent1:wo" $ nil_ $ \case
|
||||||
[ LitIntVal tn, LitIntVal n ] -> do
|
[ LitIntVal tn, LitIntVal n ] -> do
|
||||||
debug $ "ncq:concurrent1" <+> pretty tn <+> pretty n
|
debug $ "ncq:concurrent1" <+> pretty tn <+> pretty n
|
||||||
|
|
Loading…
Reference in New Issue