mirror of https://github.com/voidlizard/hbs2
wip, tcq
This commit is contained in:
parent
7196fcc54c
commit
aa3d32387d
1
Makefile
1
Makefile
|
@ -27,6 +27,7 @@ BINS := \
|
||||||
hbs2-git3 \
|
hbs2-git3 \
|
||||||
git-remote-hbs23 \
|
git-remote-hbs23 \
|
||||||
hbs2-ncq \
|
hbs2-ncq \
|
||||||
|
tcq \
|
||||||
|
|
||||||
RT_DIR := tests/RT
|
RT_DIR := tests/RT
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ import Lens.Micro.Platform
|
||||||
import Data.HashSet (HashSet)
|
import Data.HashSet (HashSet)
|
||||||
import Data.HashSet qualified as HS
|
import Data.HashSet qualified as HS
|
||||||
import Data.HashMap.Strict qualified as HM
|
import Data.HashMap.Strict qualified as HM
|
||||||
|
import System.Directory (makeAbsolute)
|
||||||
import System.FilePath.Posix
|
import System.FilePath.Posix
|
||||||
import System.Posix.Fcntl
|
import System.Posix.Fcntl
|
||||||
import System.Posix.Files qualified as Posix
|
import System.Posix.Files qualified as Posix
|
||||||
|
@ -119,6 +120,7 @@ data NCQStorage =
|
||||||
, ncqRefsDirty :: TVar Int
|
, ncqRefsDirty :: TVar Int
|
||||||
, ncqWriteQueue :: TVar (HashPSQ HashRef TimeSpec WQItem)
|
, ncqWriteQueue :: TVar (HashPSQ HashRef TimeSpec WQItem)
|
||||||
, ncqWaitIndex :: TVar (HashPSQ HashRef TimeSpec (Word64,Word64))
|
, ncqWaitIndex :: TVar (HashPSQ HashRef TimeSpec (Word64,Word64))
|
||||||
|
, ncqIndexNow :: TVar Int
|
||||||
, ncqTrackedFiles :: TVar (HashPSQ FileKey FilePrio (Maybe CachedEntry))
|
, ncqTrackedFiles :: TVar (HashPSQ FileKey FilePrio (Maybe CachedEntry))
|
||||||
, ncqCachedEntries :: TVar Int
|
, ncqCachedEntries :: TVar Int
|
||||||
, ncqNotWritten :: TVar Word64
|
, ncqNotWritten :: TVar Word64
|
||||||
|
@ -308,7 +310,8 @@ ncqStorageRun ncq@NCQStorage{..} = flip runContT pure do
|
||||||
indexer <- makeIndexer indexQ
|
indexer <- makeIndexer indexQ
|
||||||
writer <- makeWriter indexQ
|
writer <- makeWriter indexQ
|
||||||
|
|
||||||
mapM_ waitCatch [writer,indexer,refsWriter]
|
mapM_ waitCatch [writer,refsWriter]
|
||||||
|
-- mapM_ waitCatch [writer,indexer,refsWriter] -- ,indexer,refsWriter]
|
||||||
mapM_ cancel [reader]
|
mapM_ cancel [reader]
|
||||||
|
|
||||||
where
|
where
|
||||||
|
@ -381,7 +384,12 @@ ncqStorageRun ncq@NCQStorage{..} = flip runContT pure do
|
||||||
-- FIXME: timeout-hardcode
|
-- FIXME: timeout-hardcode
|
||||||
|
|
||||||
void $ race (pause @'Seconds 1) $ atomically do
|
void $ race (pause @'Seconds 1) $ atomically do
|
||||||
void $ readTQueue myFlushQ >> STM.flushTQueue myFlushQ
|
q <- tryPeekTQueue myFlushQ
|
||||||
|
s <- readTVar ncqStopped
|
||||||
|
if not (isJust q || s) then
|
||||||
|
STM.retry
|
||||||
|
else do
|
||||||
|
STM.flushTQueue myFlushQ
|
||||||
|
|
||||||
dirty <- readTVarIO ncqRefsDirty
|
dirty <- readTVarIO ncqRefsDirty
|
||||||
|
|
||||||
|
@ -405,20 +413,28 @@ ncqStorageRun ncq@NCQStorage{..} = flip runContT pure do
|
||||||
makeIndexer indexQ = do
|
makeIndexer indexQ = do
|
||||||
indexer <- ContT $ withAsync $ untilStopped do
|
indexer <- ContT $ withAsync $ untilStopped do
|
||||||
|
|
||||||
|
debug $ "STARTED INDEXER"
|
||||||
|
|
||||||
what' <- race (pause @'Seconds 1) $ atomically do
|
what' <- race (pause @'Seconds 1) $ atomically do
|
||||||
peekTQueue indexQ >> STM.flushTQueue indexQ
|
stop <- readTVar ncqStopped
|
||||||
|
q <- tryPeekTQueue indexQ
|
||||||
|
if not (stop || isJust q) then
|
||||||
|
STM.retry
|
||||||
|
else do
|
||||||
|
STM.flushTQueue indexQ
|
||||||
|
|
||||||
let what = fromRight mempty what'
|
let what = fromRight mempty what'
|
||||||
|
|
||||||
for_ what $ \(fd,fn) -> do
|
for_ what $ \(fd,fn) -> do
|
||||||
|
|
||||||
(key, added) <- ncqIndexFile ncq fn <&> over _2 HS.fromList
|
debug $ "FUCKING WRITE INDEX" <+> pretty fn
|
||||||
|
|
||||||
atomically do
|
(key, _) <- ncqIndexFile ncq fn <&> over _2 HS.fromList
|
||||||
r <- readTVar ncqWaitIndex <&> HPSQ.toList
|
|
||||||
let new = [(k,p,v) | (k,p,v) <- r, not (k `HS.member` added)]
|
|
||||||
writeTVar ncqWaitIndex (HPSQ.fromList new)
|
|
||||||
|
|
||||||
|
-- atomically do
|
||||||
|
-- r <- readTVar ncqWaitIndex <&> HPSQ.toList
|
||||||
|
-- let new = [(k,p,v) | (k,p,v) <- r, not (k `HS.member` added)]
|
||||||
|
-- writeTVar ncqWaitIndex (HPSQ.fromList new)
|
||||||
|
|
||||||
ncqAddTrackedFilesIO ncq [key]
|
ncqAddTrackedFilesIO ncq [key]
|
||||||
atomically do
|
atomically do
|
||||||
|
@ -489,7 +505,9 @@ ncqStorageRun ncq@NCQStorage{..} = flip runContT pure do
|
||||||
|
|
||||||
writeBinaryFileDurable (ncqGetCurrentSizeName ncq) (N.bytestring64 (fromIntegral size))
|
writeBinaryFileDurable (ncqGetCurrentSizeName ncq) (N.bytestring64 (fromIntegral size))
|
||||||
|
|
||||||
when (fromIntegral size >= ncqMinLog) do
|
indexNow <- readTVarIO ncqIndexNow
|
||||||
|
|
||||||
|
when (fromIntegral size >= ncqMinLog || indexNow > 0) do
|
||||||
|
|
||||||
(n,u) <- atomically do
|
(n,u) <- atomically do
|
||||||
r <- readTVar ncqCurrentHandleR <&> fromIntegral
|
r <- readTVar ncqCurrentHandleR <&> fromIntegral
|
||||||
|
@ -505,6 +523,7 @@ ncqStorageRun ncq@NCQStorage{..} = flip runContT pure do
|
||||||
mv current fossilized
|
mv current fossilized
|
||||||
|
|
||||||
atomically do
|
atomically do
|
||||||
|
writeTVar ncqIndexNow 0
|
||||||
r <- readTVar ncqCurrentHandleR
|
r <- readTVar ncqCurrentHandleR
|
||||||
-- NOTE: extra-use
|
-- NOTE: extra-use
|
||||||
-- добавляем лишний 1 для индексации.
|
-- добавляем лишний 1 для индексации.
|
||||||
|
@ -831,7 +850,8 @@ ncqFixIndexes ncq@NCQStorage{..} = do
|
||||||
|
|
||||||
|
|
||||||
ncqStorageOpen :: MonadUnliftIO m => FilePath -> m NCQStorage
|
ncqStorageOpen :: MonadUnliftIO m => FilePath -> m NCQStorage
|
||||||
ncqStorageOpen fp = do
|
ncqStorageOpen fp' = do
|
||||||
|
fp <- liftIO $ makeAbsolute fp'
|
||||||
ncq@NCQStorage{..} <- ncqStorageInit_ False fp
|
ncq@NCQStorage{..} <- ncqStorageInit_ False fp
|
||||||
ncqReadTrackedFiles ncq
|
ncqReadTrackedFiles ncq
|
||||||
ncqFixIndexes ncq
|
ncqFixIndexes ncq
|
||||||
|
@ -926,6 +946,7 @@ ncqStorageInit_ check path = do
|
||||||
ncqStopped <- newTVarIO False
|
ncqStopped <- newTVarIO False
|
||||||
ncqTrackedFiles <- newTVarIO HPSQ.empty
|
ncqTrackedFiles <- newTVarIO HPSQ.empty
|
||||||
ncqCachedEntries <- newTVarIO 0
|
ncqCachedEntries <- newTVarIO 0
|
||||||
|
ncqIndexNow <- newTVarIO 0
|
||||||
|
|
||||||
let currentName = ncqGetCurrentName_ path ncqGen
|
let currentName = ncqGetCurrentName_ path ncqGen
|
||||||
|
|
||||||
|
@ -973,6 +994,9 @@ ncqStorageInit_ check path = do
|
||||||
|
|
||||||
pure ncq
|
pure ncq
|
||||||
|
|
||||||
|
ncqIndexRightNow :: MonadUnliftIO m => NCQStorage -> m ()
|
||||||
|
ncqIndexRightNow NCQStorage{..} = atomically $ modifyTVar ncqIndexNow succ
|
||||||
|
|
||||||
withNCQ :: forall m a . MonadUnliftIO m
|
withNCQ :: forall m a . MonadUnliftIO m
|
||||||
=> (NCQStorage -> NCQStorage)
|
=> (NCQStorage -> NCQStorage)
|
||||||
-> FilePath
|
-> FilePath
|
||||||
|
|
|
@ -1200,3 +1200,24 @@ executable test-cq-storage
|
||||||
, unix
|
, unix
|
||||||
|
|
||||||
|
|
||||||
|
executable tcq
|
||||||
|
import: shared-properties
|
||||||
|
import: common-deps
|
||||||
|
default-language: Haskell2010
|
||||||
|
ghc-options:
|
||||||
|
hs-source-dirs: test
|
||||||
|
main-is: TCQ.hs
|
||||||
|
build-depends:
|
||||||
|
base, hbs2-core, hbs2-log-structured, hbs2-storage-ncq
|
||||||
|
, network
|
||||||
|
, string-conversions
|
||||||
|
, db-pipe
|
||||||
|
, suckless-conf
|
||||||
|
, network-byte-order
|
||||||
|
, text
|
||||||
|
, time
|
||||||
|
, mmap
|
||||||
|
, zstd
|
||||||
|
, unix
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,219 @@
|
||||||
|
{-# Language AllowAmbiguousTypes #-}
|
||||||
|
{-# Language UndecidableInstances #-}
|
||||||
|
{-# Language MultiWayIf #-}
|
||||||
|
{-# Language RecordWildCards #-}
|
||||||
|
{-# Language ViewPatterns #-}
|
||||||
|
module Main where
|
||||||
|
|
||||||
|
import HBS2.Prelude.Plated
|
||||||
|
import HBS2.OrDie
|
||||||
|
import HBS2.Hash
|
||||||
|
import HBS2.Data.Types.Refs
|
||||||
|
import HBS2.Clock
|
||||||
|
import HBS2.Merkle
|
||||||
|
|
||||||
|
import HBS2.Storage
|
||||||
|
import HBS2.Storage.Simple
|
||||||
|
import HBS2.Storage.Operations.ByteString
|
||||||
|
|
||||||
|
import HBS2.System.Logger.Simple.ANSI
|
||||||
|
|
||||||
|
import HBS2.Storage.NCQ
|
||||||
|
import HBS2.Data.Log.Structured.NCQ
|
||||||
|
|
||||||
|
|
||||||
|
import Data.Config.Suckless.Syntax
|
||||||
|
import Data.Config.Suckless.Script
|
||||||
|
import Data.Config.Suckless.System
|
||||||
|
|
||||||
|
import DBPipe.SQLite hiding (field)
|
||||||
|
|
||||||
|
import Data.Bits
|
||||||
|
import Data.ByteString (ByteString)
|
||||||
|
import Data.ByteString qualified as BS
|
||||||
|
import Data.ByteString.Lazy qualified as LBS
|
||||||
|
import Data.Text.Encoding qualified as TE
|
||||||
|
import Data.ByteString.Char8 qualified as BS8
|
||||||
|
import Data.ByteString.Builder
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Word
|
||||||
|
import Data.List qualified as List
|
||||||
|
import Data.Vector qualified as V
|
||||||
|
import Data.Vector ((!))
|
||||||
|
import Control.Monad.Trans.Cont
|
||||||
|
import Control.Monad.Trans.Maybe
|
||||||
|
import Network.ByteOrder qualified as N
|
||||||
|
import Data.Coerce
|
||||||
|
import Data.HashPSQ qualified as HPSQ
|
||||||
|
import Data.HashSet qualified as HS
|
||||||
|
import Data.HashMap.Strict (HashMap)
|
||||||
|
import Data.HashMap.Strict qualified as HM
|
||||||
|
import Data.IntMap qualified as IntMap
|
||||||
|
import Data.IntMap (IntMap)
|
||||||
|
import Data.Fixed
|
||||||
|
import System.Environment
|
||||||
|
import System.Directory
|
||||||
|
import System.Posix.Fcntl
|
||||||
|
import System.Posix.IO
|
||||||
|
import System.IO.MMap
|
||||||
|
import System.IO qualified as IO
|
||||||
|
import System.Exit (exitSuccess, exitFailure)
|
||||||
|
import System.Random
|
||||||
|
import Safe
|
||||||
|
import Lens.Micro.Platform
|
||||||
|
import Control.Concurrent.STM qualified as STM
|
||||||
|
|
||||||
|
import UnliftIO
|
||||||
|
|
||||||
|
import Text.InterpolatedString.Perl6 (qc)
|
||||||
|
|
||||||
|
import Streaming.Prelude qualified as S
|
||||||
|
import System.TimeIt
|
||||||
|
|
||||||
|
import System.IO.Unsafe (unsafePerformIO)
|
||||||
|
|
||||||
|
{- HLINT ignore "Functor law" -}
|
||||||
|
|
||||||
|
setupLogger :: MonadIO m => m ()
|
||||||
|
setupLogger = do
|
||||||
|
-- setLogging @DEBUG $ toStderr . logPrefix "[debug] "
|
||||||
|
setLogging @ERROR $ toStderr . logPrefix "[error] "
|
||||||
|
setLogging @WARN $ toStderr . logPrefix "[warn] "
|
||||||
|
setLogging @NOTICE $ toStdout . logPrefix ""
|
||||||
|
|
||||||
|
flushLoggers :: MonadIO m => m ()
|
||||||
|
flushLoggers = do
|
||||||
|
silence
|
||||||
|
|
||||||
|
silence :: MonadIO m => m ()
|
||||||
|
silence = do
|
||||||
|
setLoggingOff @DEBUG
|
||||||
|
setLoggingOff @ERROR
|
||||||
|
setLoggingOff @WARN
|
||||||
|
setLoggingOff @NOTICE
|
||||||
|
setLoggingOff @TRACE
|
||||||
|
|
||||||
|
|
||||||
|
data TCQError =
|
||||||
|
TCQAlreadyOpen FilePath
|
||||||
|
| TCQGone FilePath
|
||||||
|
deriving stock (Show,Typeable)
|
||||||
|
|
||||||
|
instance Exception TCQError
|
||||||
|
|
||||||
|
newtype TCQ =
|
||||||
|
TCQ FilePath
|
||||||
|
deriving newtype (Eq,Ord,Show,Typeable)
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = do
|
||||||
|
|
||||||
|
instances <- newTVarIO (mempty :: HashMap FilePath (NCQStorage, Async ()))
|
||||||
|
|
||||||
|
tvd <- newTVarIO mempty
|
||||||
|
|
||||||
|
let finalizeStorages = do
|
||||||
|
debug "finalize ncq"
|
||||||
|
r <- readTVarIO instances <&> HM.toList
|
||||||
|
mapM_ ncqStorageStop (fmap (fst.snd) r)
|
||||||
|
mapM_ wait (fmap (snd.snd) r)
|
||||||
|
|
||||||
|
let getNCQ (TCQ p) = do
|
||||||
|
readTVarIO instances
|
||||||
|
<&> HM.lookup p
|
||||||
|
<&> fmap fst
|
||||||
|
>>= orThrow (TCQGone p)
|
||||||
|
|
||||||
|
let dict = makeDict @C do
|
||||||
|
|
||||||
|
entry $ bindMatch "--help" $ nil_ \case
|
||||||
|
HelpEntryBound what -> helpEntry what
|
||||||
|
[StringLike s] -> helpList False (Just s)
|
||||||
|
_ -> helpList False Nothing
|
||||||
|
|
||||||
|
internalEntries
|
||||||
|
|
||||||
|
entry $ bindMatch "--run" $ \case
|
||||||
|
[ StringLike what ] -> liftIO do
|
||||||
|
liftIO (readFile what)
|
||||||
|
<&> parseTop
|
||||||
|
>>= either (error.show) pure
|
||||||
|
>>= runEval tvd
|
||||||
|
|
||||||
|
e -> throwIO $ BadFormException @C (mkList e)
|
||||||
|
|
||||||
|
entry $ bindMatch "debug" $ nil_ \case
|
||||||
|
|
||||||
|
[ LitBoolVal False ] -> do
|
||||||
|
setLoggingOff @DEBUG
|
||||||
|
|
||||||
|
[ StringLike "off" ] -> do
|
||||||
|
setLoggingOff @DEBUG
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
setLogging @DEBUG $ toStderr . logPrefix "[debug] "
|
||||||
|
|
||||||
|
entry $ bindMatch "ncq:open" $ \case
|
||||||
|
[ StringLike path ] -> do
|
||||||
|
debug $ "ncq:open" <+> pretty path
|
||||||
|
ncq <- ncqStorageOpen path
|
||||||
|
r <- async (ncqStorageRun ncq)
|
||||||
|
|
||||||
|
e <- atomically do
|
||||||
|
already <- readTVar instances <&> HM.member path
|
||||||
|
if already then
|
||||||
|
pure $ Left $ TCQAlreadyOpen path
|
||||||
|
else do
|
||||||
|
modifyTVar instances (HM.insert path (ncq,r))
|
||||||
|
pure $ Right ()
|
||||||
|
|
||||||
|
either throwIO pure e
|
||||||
|
|
||||||
|
mkOpaque (TCQ path)
|
||||||
|
|
||||||
|
e -> throwIO $ BadFormException @C (mkList e)
|
||||||
|
|
||||||
|
entry $ bindMatch "ncq:poke" $ \case
|
||||||
|
[ isOpaqueOf @TCQ -> Just tcq ] -> lift do
|
||||||
|
ncq <- getNCQ tcq
|
||||||
|
pure $ mkSym "okay"
|
||||||
|
|
||||||
|
e -> throwIO $ BadFormException @C (mkList e)
|
||||||
|
|
||||||
|
entry $ bindMatch "ncq:get" $ \case
|
||||||
|
[ isOpaqueOf @TCQ -> Just tcq, HashLike hash ] -> lift do
|
||||||
|
ncq <- getNCQ tcq
|
||||||
|
ncqStorageGet ncq hash >>= maybe (pure nil) mkOpaque
|
||||||
|
|
||||||
|
e -> throwIO $ BadFormException @C (mkList e)
|
||||||
|
|
||||||
|
entry $ bindMatch "ncq:put" $ \syn -> do
|
||||||
|
(tcq,bs) <- case syn of
|
||||||
|
[ isOpaqueOf @TCQ -> Just tcq, isOpaqueOf @ByteString -> Just bs ] -> lift do
|
||||||
|
pure (tcq, LBS.fromStrict bs)
|
||||||
|
|
||||||
|
[ isOpaqueOf @TCQ -> Just tcq, TextLike s ] -> lift do
|
||||||
|
pure (tcq, LBS.fromStrict (TE.encodeUtf8 s))
|
||||||
|
|
||||||
|
e -> throwIO $ BadFormException @C (mkList e)
|
||||||
|
|
||||||
|
lift do
|
||||||
|
ncq <- getNCQ tcq
|
||||||
|
r <- ncqStoragePut ncq bs
|
||||||
|
pure $ maybe nil (mkSym . show . pretty) r
|
||||||
|
|
||||||
|
|
||||||
|
setupLogger
|
||||||
|
|
||||||
|
argz <- liftIO getArgs
|
||||||
|
|
||||||
|
forms <- parseTop (unlines $ unwords <$> splitForms argz)
|
||||||
|
& either (error.show) pure
|
||||||
|
|
||||||
|
atomically $ writeTVar tvd dict
|
||||||
|
|
||||||
|
(runEval tvd forms >>= eatNil display)
|
||||||
|
`finally` (finalizeStorages >> flushLoggers)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,15 @@ main = do
|
||||||
|
|
||||||
internalEntries
|
internalEntries
|
||||||
|
|
||||||
|
entry $ bindMatch "run" $ \case
|
||||||
|
[ StringLike what ] -> do
|
||||||
|
liftIO (readFile what)
|
||||||
|
<&> parseTop
|
||||||
|
>>= either (error.show) pure
|
||||||
|
>>= evalTop
|
||||||
|
|
||||||
|
e -> throwIO $ BadFormException @C (mkList e)
|
||||||
|
|
||||||
entry $ bindMatch "test:sqlite" $ nil_ $ \case
|
entry $ bindMatch "test:sqlite" $ nil_ $ \case
|
||||||
[StringLike fn] -> liftIO do
|
[StringLike fn] -> liftIO do
|
||||||
hashes <- readFile fn <&> mapMaybe (fromStringMay @HashRef) . lines
|
hashes <- readFile fn <&> mapMaybe (fromStringMay @HashRef) . lines
|
||||||
|
@ -514,6 +523,17 @@ main = do
|
||||||
|
|
||||||
e -> throwIO $ BadFormException @C (mkList e)
|
e -> throwIO $ BadFormException @C (mkList e)
|
||||||
|
|
||||||
|
|
||||||
|
entry $ bindMatch "test:ncq:index:now" $ nil_ \case
|
||||||
|
[StringLike p] -> lift do
|
||||||
|
withNCQ id p $ \ncq -> do
|
||||||
|
display_ $ "test:ncq:index:now" <+> pretty p
|
||||||
|
ncqIndexRightNow ncq
|
||||||
|
pause @'Seconds 10
|
||||||
|
display_ $ "test:ncq:index:now" <+> pretty p <+> "done"
|
||||||
|
|
||||||
|
e -> throwIO $ BadFormException @C (mkList e)
|
||||||
|
|
||||||
entry $ bindMatch "test:ncq:run" $ nil_ \case
|
entry $ bindMatch "test:ncq:run" $ nil_ \case
|
||||||
[StringLike p] -> lift do
|
[StringLike p] -> lift do
|
||||||
withNCQ id p $ \_ -> do
|
withNCQ id p $ \_ -> do
|
||||||
|
|
Loading…
Reference in New Issue