This commit is contained in:
voidlizard 2025-07-29 11:37:34 +03:00
parent 7365aa3813
commit 4b003fe2ec
10 changed files with 269 additions and 128 deletions

View File

@ -55,7 +55,7 @@ common shared-properties
, TypeFamilies
, TypeOperators
, RecordWildCards
, OverloadedLabels
library
import: shared-properties
@ -81,13 +81,16 @@ library
build-depends: base, hbs2-core, hbs2-log-structured, suckless-conf
, async
, binary
, bitvec
, bytestring
, bytestring-mmap
, bitvec
, containers
, directory
, filelock
, filepath
, filepattern
, generic-lens
-- , generic-optics
, hashable
, memory
, microlens-platform
@ -95,6 +98,8 @@ library
, mtl
, mwc-random
, network-byte-order
, optics-core
, optics
, prettyprinter
, psqueues
, random
@ -104,8 +109,8 @@ library
, stm-chans
, streaming
, temporary
, time
, text
, time
, transformers
, uniplate
, unix
@ -113,8 +118,6 @@ library
, unordered-containers
, vector
, zstd
, filelock
hs-source-dirs: lib
default-language: Haskell2010

View File

@ -53,8 +53,10 @@ instance Pretty FileKey where
pretty (FileKey s) = pretty (BS8.unpack s)
newtype DataFile a = DataFile a
deriving newtype (IsString,Pretty)
newtype IndexFile a = IndexFile a
deriving newtype (IsString,Pretty)
newtype StateFile a = StateFile a
deriving newtype (IsString,Eq,Ord,Pretty)
@ -62,6 +64,9 @@ newtype StateFile a = StateFile a
class ToFileName a where
toFileName :: a -> FilePath
instance ToFileName FilePath where
toFileName = id
instance ToFileName FileKey where
toFileName = BS8.unpack . coerce

View File

@ -7,12 +7,15 @@ import HBS2.Storage.NCQ3.Internal.State
import HBS2.Storage.NCQ3.Internal.Run
import HBS2.Storage.NCQ3.Internal.Memtable
import HBS2.Storage.NCQ3.Internal.Files
import HBS2.Storage.NCQ3.Internal.Index
import Control.Monad.Trans.Cont
import Network.ByteOrder qualified as N
import Data.HashPSQ qualified as HPSQ
import Data.Vector qualified as V
import Data.HashMap.Strict qualified as HM
import Data.List qualified as List
import Data.Set qualified as Set
import Data.ByteString qualified as BS
import Data.Sequence qualified as Seq
import System.FilePath.Posix
@ -56,12 +59,6 @@ ncqStorageOpen3 fp upd = do
ncqMemTable <- V.fromList <$> replicateM shardNum (newTVarIO mempty)
ncqMMapCachedIdx <- newTVarIO HPSQ.empty
ncqMMapCachedData <- newTVarIO HPSQ.empty
ncqStateFiles <- newTVarIO mempty
ncqStateIndex <- newTVarIO mempty
ncqStateFileSeq <- newTVarIO 0
ncqStateVersion <- newTVarIO 0
ncqStateUsage <- newTVarIO mempty
ncqStateFacts <- newTVarIO mempty
ncqWrites <- newTVarIO 0
ncqWriteEMA <- newTVarIO 0.0
ncqWriteOps <- V.fromList <$> replicateM wopNum newTQueueIO
@ -71,6 +68,7 @@ ncqStorageOpen3 fp upd = do
ncqSyncReq <- newTVarIO False
ncqOnRunWriteIdle <- newTVarIO none
ncqSyncNo <- newTVarIO 0
ncqState <- newTVarIO mempty
let ncq = NCQStorage3{..} & upd
@ -134,3 +132,67 @@ ncqLocate me@NCQStorage3{..} href = ncqOperation me (pure Nothing) do
atomically $ takeTMVar answ
ncqTryLoadState :: forall m. MonadUnliftIO m
=> NCQStorage3
-> m ()
ncqTryLoadState me = do
stateFiles <- ncqListFilesBy me ( List.isPrefixOf "s-" )
r <- flip fix ([], ncqState0, stateFiles) $ \next -> \case
(r, s, []) -> pure (r,s,[])
(l, s0, (_,s):ss) -> do
readStateMay me s >>= \case
Nothing -> next (s : l, s0, ss)
Just ns -> do
ok <- checkState ns
if ok then
pure (l <> fmap snd ss, ns, ss)
else
next (s : l, s0, ss)
let (bad, NCQState{..}, rest) = r
for_ [ (d,s) | P (PData d s) <- Set.toList ncqStateFacts ] $ \(dataFile,s) -> do
let path = ncqGetFileName me dataFile
realSize <- fileSize path
let corrupted = realSize /= fromIntegral s
let color = if corrupted then red else id
debug $ yellow "indexing" <+> pretty dataFile <+> pretty s <+> color (pretty realSize)
when corrupted $ liftIO do
warn $ red "trim" <+> pretty s <+> pretty (takeFileName path)
PFS.setFileSize path (fromIntegral s)
ncqIndexFile me dataFile
for_ (bad <> drop 3 (fmap snd rest)) $ \f -> do
rm (ncqGetFileName me (StateFile f))
where
-- TODO: created-but-not-indexed-file?
checkState NCQState{..} = flip runContT pure $ callCC \exit -> do
for_ ncqStateFiles $ \fk -> do
let dataFile = ncqGetFileName me (DataFile fk)
here <- doesFileExist dataFile
unless here $ exit False
lift (try @_ @SomeException (ncqFileFastCheck dataFile)) >>= \case
Left e -> err (viaShow e) >> exit False
Right () -> none
pure True

View File

@ -1,3 +1,4 @@
{-# Language OverloadedRecordDot #-}
module HBS2.Storage.NCQ3.Internal.Files where
import HBS2.Storage.NCQ3.Internal.Prelude
@ -6,8 +7,9 @@ import HBS2.Storage.NCQ3.Internal.Types
import System.Posix.Files qualified as PFS
import Data.List qualified as List
ncqGetFileName :: NCQStorage3 -> FilePath -> FilePath
ncqGetFileName ncq fp = ncqGetWorkDir ncq </> takeFileName fp
ncqGetFileName :: forall f . ToFileName f => NCQStorage3 -> f -> FilePath
ncqGetFileName ncq fp = ncqGetWorkDir ncq </> takeFileName (toFileName fp)
ncqGetWorkDir :: NCQStorage3 -> FilePath
ncqGetWorkDir NCQStorage3{..} = ncqRoot </> show ncqGen
@ -20,10 +22,11 @@ ncqGetNewFileKey :: forall f m . (ToFileName f, MonadIO m)
-> ( FileKey -> f )
-> m FileKey
ncqGetNewFileKey me@NCQStorage3{..} fnameOf = fix \next -> do
n <- atomically $ stateTVar ncqStateFileSeq (\x -> (x, succ x))
here <- doesFileExist (ncqGetFileName me (toFileName $ fnameOf n))
n <- atomically $ stateTVar ncqState (\e -> (e.ncqStateFileSeq , succSeq e))
here <- doesFileExist (ncqGetFileName me (fnameOf n))
if here then next else pure n
where
succSeq e = e { ncqStateFileSeq = succ e.ncqStateFileSeq }
ncqListFilesBy :: forall m . MonadUnliftIO m => NCQStorage3 -> (FilePath -> Bool) -> m [(POSIXTime, FileKey)]
ncqListFilesBy me@NCQStorage3{..} filt = do

View File

@ -46,7 +46,7 @@ ncqIndexFile n@NCQStorage3{..} fk = runMaybeT do
let fp = toFileName fk & ncqGetFileName n
fki <- ncqGetNewFileKey n IndexFile
let dest = ncqGetFileName n (toFileName (IndexFile fki))
let dest = ncqGetFileName n (IndexFile fki)
debug $ "INDEX" <+> pretty fp <+> pretty dest
@ -81,6 +81,7 @@ ncqIndexFile n@NCQStorage3{..} fk = runMaybeT do
ncqStateAddIndexFile ts fki
ncqStateAddDataFile (coerce fk)
ncqStateAddFact (FI fk (IndexFile fki))
ncqStateDelFact (P (PData fk 0))
(bs,nw) <- toMPlus midx

View File

@ -38,7 +38,7 @@ ncqGetCachedData ncq@NCQStorage3{..} =
cacheLookupOrInsert ncqMaxCachedData load ncqMMapCachedData
where
load fk = do
let path = ncqGetFileName ncq (toFileName (DataFile fk))
let path = ncqGetFileName ncq (DataFile fk)
bs <- liftIO (mmapFileByteString path Nothing)
pure (CachedData bs)
@ -47,7 +47,7 @@ ncqGetCachedIndex ncq@NCQStorage3{..} =
cacheLookupOrInsert ncqMaxCachedIndex load ncqMMapCachedIdx
where
load fk = do
let path = ncqGetFileName ncq (toFileName (IndexFile fk))
let path = ncqGetFileName ncq (IndexFile fk)
nwayHashMMapReadOnly path >>= \case
Nothing -> throwIO $ NCQStorageCantMapFile path
Just (bs, nway) -> pure (CachedIndex bs nway)

View File

@ -7,6 +7,7 @@ import HBS2.Storage.NCQ3.Internal.Types
import HBS2.Storage.NCQ3.Internal.Files
import HBS2.Storage.NCQ3.Internal.Memtable
import HBS2.Storage.NCQ3.Internal.Index
import HBS2.Storage.NCQ3.Internal.State
import HBS2.Storage.NCQ3.Internal.MMapCache
@ -74,9 +75,9 @@ ncqStorageRun3 ncq@NCQStorage3{..} = flip runContT pure do
Nothing -> none
Just e -> answer (Just (InMemory (ncqEntryData e))) >> next
tracked <- readTVarIO ncqStateIndex
NCQState{..} <- readTVarIO ncqState
for_ tracked $ \(_, fk) -> do
for_ ncqStateIndex $ \(_, fk) -> do
CachedIndex bs nw <- ncqGetCachedIndex ncq fk
ncqLookupIndex h (bs, nw) >>= \case
Just (IndexEntry fk o s) -> answer (Just (InFossil fk o s)) >> next
@ -112,6 +113,10 @@ ncqStorageRun3 ncq@NCQStorage3{..} = flip runContT pure do
pure w
else do
appendTailSection fh >> liftIO (fileSynchronise fh)
ss <- liftIO (PFS.getFdStatus fh) <&> fromIntegral . PFS.fileSize
ncqStateUpdate ncq do
ncqStateAddFact (P (PData (DataFile fk) ss))
atomically do
writeTVar ncqSyncReq False
modifyTVar ncqSyncNo succ
@ -173,7 +178,7 @@ ncqStorageRun3 ncq@NCQStorage3{..} = flip runContT pure do
openNewDataFile :: forall mx . MonadIO mx => mx (FileKey, Fd)
openNewDataFile = do
fk <- ncqGetNewFileKey ncq DataFile
let fname = ncqGetFileName ncq (toFileName (DataFile fk))
let fname = ncqGetFileName ncq (DataFile fk)
touch fname
let flags = defaultFileFlags { exclusive = False, creat = Just 0o666 }
(fk,) <$> liftIO (PosixBase.openFd fname Posix.ReadWrite flags)

View File

@ -1,3 +1,4 @@
{-# Language ViewPatterns #-}
module HBS2.Storage.NCQ3.Internal.State where
import HBS2.Storage.NCQ3.Internal.Prelude
@ -6,8 +7,10 @@ import HBS2.Storage.NCQ3.Internal.Files
import Data.Config.Suckless.Script
import Data.Generics.Product
import Data.List qualified as List
import Control.Monad.Reader
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Cont
import Data.HashSet qualified as HS
import Data.Set qualified as Set
@ -16,6 +19,8 @@ import UnliftIO.IO.File
import Network.ByteOrder qualified as N
import UnliftIO.IO
import System.IO qualified as IO
import Lens.Micro.Platform
import Streaming.Prelude qualified as S
newtype StateOP a =
StateOP { fromStateOp :: ReaderT NCQStorage3 STM a }
@ -28,38 +33,34 @@ ncqStateUpdate :: MonadIO m
-> StateOP a
-> m ()
ncqStateUpdate ncq@NCQStorage3{..} action = do
snkFile <- ncqGetNewFileKey ncq StateFile <&> ncqGetFileName ncq . toFileName . StateFile
(n,i,f,facts) <- atomically do
runReaderT (fromStateOp action) ncq
n <- readTVar ncqStateFileSeq
i <- readTVar ncqStateIndex
f <- readTVar ncqStateFiles
fa <- readTVar ncqStateFacts
pure (n,i,f,fa)
s0 <- readTVarIO ncqState
liftIO $ withBinaryFileDurableAtomic snkFile WriteMode $ \fh -> do
for_ i $ \(Down p, fk) -> do
IO.hPrint fh $ "i" <+> pretty fk <+> pretty (round @_ @Word64 p)
s1 <- atomically do
void $ runReaderT (fromStateOp action) ncq
readTVar ncqState
for_ f $ \fk -> do
IO.hPrint fh $ "f" <+> pretty fk
for_ facts $ \(FI (DataFile a) (IndexFile b)) -> do
IO.hPrint fh $ "fi" <+> pretty a <+> pretty b
IO.hPrint fh $ "n" <+> pretty n
unless (s1 == s0) do
snkFile <- ncqGetNewFileKey ncq StateFile <&> ncqGetFileName ncq . StateFile
liftIO $ withBinaryFileDurableAtomic snkFile WriteMode $ \fh -> do
IO.hPrint fh (pretty s1)
ncqStateAddDataFile :: FileKey -> StateOP ()
ncqStateAddDataFile fk = do
NCQStorage3{..} <- ask
StateOP $ lift do
modifyTVar ncqStateFiles (HS.insert fk)
modifyTVar ncqState (over (field @"ncqStateFiles") (HS.insert fk))
ncqStateAddFact :: Fact -> StateOP ()
ncqStateAddFact fact = do
NCQStorage3{..} <- ask
StateOP $ lift do
modifyTVar ncqStateFacts (Set.insert fact)
modifyTVar ncqState (over (field @"ncqStateFacts") (Set.insert fact))
ncqStateDelFact :: Fact -> StateOP ()
ncqStateDelFact fact = do
NCQStorage3{..} <- ask
StateOP $ lift do
modifyTVar ncqState (over (field @"ncqStateFacts") (Set.delete fact))
ncqStateAddIndexFile :: POSIXTime
-> FileKey
@ -67,10 +68,10 @@ ncqStateAddIndexFile :: POSIXTime
ncqStateAddIndexFile ts fk = do
NCQStorage3{..} <- ask
StateOP $ lift do
modifyTVar' ncqStateIndex $ \xs ->
List.sortOn fst ((Down ts, fk) : xs)
StateOP $ lift $ modifyTVar' ncqState sortIndexes
sortIndexes :: NCQState -> NCQState
sortIndexes = over (field @"ncqStateIndex") (List.sortOn fst)
ncqFileFastCheck :: MonadUnliftIO m => FilePath -> m ()
ncqFileFastCheck fp = do
@ -85,73 +86,38 @@ ncqFileFastCheck fp = do
throwIO $ NCQFsckIssueExt (FsckInvalidFileSize (fromIntegral s))
ncqTryLoadState :: forall m. MonadUnliftIO m
readStateMay :: forall m . MonadUnliftIO m
=> NCQStorage3
-> m ()
-> FileKey
-> m (Maybe NCQState)
readStateMay sto key = fmap sortIndexes <$> do
s <- liftIO (readFile (ncqGetFileName sto (StateFile key)))
runMaybeT do
sexps <- parseTop s & toMPlus
ncqTryLoadState me@NCQStorage3{..} = do
flip fix (ncqState0, sexps) $ \next -> \case
(acc, []) -> pure acc
(acc, e : ss) -> liftIO (print (pretty e)) >> next (acc <> entryOf e, ss)
stateFiles <- ncqListFilesBy me ( List.isPrefixOf "s-" )
where
flip runContT pure $ callCC \exit -> do
entryOf = \case
ListVal [SymbolVal "i", LitIntVal n, LitIntVal ts] ->
ncqState0 { ncqStateIndex = [ (fromIntegral ts, fromIntegral n) ] }
for stateFiles $ \(_,fn) -> do
none
ListVal [SymbolVal "f", LitIntVal n] ->
ncqState0 { ncqStateFiles = HS.singleton (fromIntegral n) }
none
ListVal [SymbolVal "fi", LitIntVal a, LitIntVal b] ->
ncqState0 { ncqStateFacts = Set.singleton (FI (DataFile (fromIntegral a)) (IndexFile (fromIntegral b))) }
-- for_ stateFiles $ \(d,f) -> do
-- notice $ "state-file" <+> pretty (toFileName (StateFile f))
ListVal [SymbolVal "fp", LitIntVal a, LitIntVal s] ->
ncqState0 { ncqStateFacts = Set.singleton (P (PData (DataFile $ fromIntegral a) (fromIntegral s))) }
-- tryLoadState :: forall m. MonadUnliftIO m
-- => NCQStorage3
-- -> StateFile FileKey
-- -> m (Maybe (HashSet FileKey, [(Down POSIXTime, FileKey)], FileKey))
-- tryLoadState me@NCQStorage3{..} fk = do
-- debug $ "tryLoadState" <+> pretty fk
ListVal [SymbolVal "n", LitIntVal a] ->
ncqState0 { ncqStateFileSeq = fromIntegral a }
-- (fset, idxList, n) <- liftIO (readState fk)
-- let checkFile :: DataFile FileKey -> m Bool
-- checkFile fo = flip fix 0 \next (i :: Int) -> do
-- let dataFile = ncqGetFileName me (toFileName fo)
-- let indexFile = ncqGetFileName me (toFileName (IndexFile (coerce fo)))
-- doesFileExist dataFile >>= \case
-- False -> do
-- rm indexFile
-- pure False
-- True -> do
-- try @_ @SomeException (ncqFileFastCheck dataFile) >>= \case
-- Left e -> do
-- err (viaShow e)
-- stillThere <- doesFileExist dataFile
-- when stillThere do
-- let broken = dropExtension dataFile `addExtension` ".broken"
-- mv dataFile broken
-- rm indexFile
-- warn $ red "renamed" <+> pretty dataFile <+> pretty broken
-- pure False
-- Right{} | i > 1 -> pure False
-- Right{} -> do
-- exists <- doesFileExist indexFile
-- if exists
-- then pure True
-- else do
-- debug $ "indexing" <+> pretty (toFileName fo)
-- _ <- ncqIndexFile me fo
-- debug $ "indexed" <+> pretty indexFile
-- next (i + 1)
-- results <- forM (HS.toList fset) (checkFile . DataFile)
-- pure $
-- if and results
-- then Just (fset, idxList, n)
-- else Nothing
_ -> ncqState0

View File

@ -1,9 +1,16 @@
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE UndecidableInstances #-}
module HBS2.Storage.NCQ3.Internal.Types where
import HBS2.Storage.NCQ3.Internal.Prelude
import Data.Generics.Product
import Numeric (readHex)
import Data.Set qualified as Set
import Data.HashSet qualified as HS
import Text.Printf
-- import Lens.Micro.Platform
data CachedData = CachedData !ByteString
data CachedIndex = CachedIndex !ByteString !NWayHash
@ -16,25 +23,13 @@ type Shard = TVar (HashMap HashRef NCQEntry)
type StateVersion = Word64
newtype FileKey = FileKey Word32
deriving newtype (Eq,Ord,Show,Num,Enum,Pretty,Hashable)
deriving newtype (Eq,Ord,Show,Num,Enum,Real,Integral,Pretty,Hashable)
deriving stock instance Eq (DataFile FileKey)
deriving stock instance Ord (DataFile FileKey)
deriving stock instance Eq (IndexFile FileKey)
deriving stock instance Ord (IndexFile FileKey)
instance IsString FileKey where
fromString = FileKey . maybe maxBound fst . headMay . readHex . drop 1 . dropWhile (/= '-') . takeBaseName
instance ToFileName (DataFile FileKey) where
toFileName (DataFile fk) = ncqMakeFossilName fk
instance ToFileName (IndexFile FileKey) where
toFileName (IndexFile fk) = printf "i-%08x.cq" (coerce @_ @Word32 fk)
instance ToFileName (StateFile FileKey) where
toFileName (StateFile fk) = printf "s-%08x" (coerce @_ @Word32 fk)
data NCQEntry =
NCQEntry
{ ncqEntryData :: !ByteString
@ -48,15 +43,30 @@ data Location =
InFossil {-# UNPACK #-} !FileKey !NCQOffset !NCQSize
| InMemory {-# UNPACK #-} !ByteString
instance Pretty Location where
pretty = \case
InFossil k o s -> parens $ "in-fossil" <+> pretty k <+> pretty o <+> pretty s
InMemory _ -> "in-memory"
data Fact =
FI (DataFile FileKey) (IndexFile FileKey) -- file X has index Y
FI (DataFile FileKey) (IndexFile FileKey) -- file X has index Y
| P PData -- pending, not indexed
deriving stock (Eq,Ord)
data PData = PData (DataFile FileKey) Word64
instance Ord PData where
compare (PData a _) (PData b _) = compare a b
instance Eq PData where
(==) (PData a _) (PData b _) = a == b
data NCQState =
NCQState
{ ncqStateFiles :: HashSet FileKey
, ncqStateIndex :: [(Down POSIXTime, FileKey)] -- backward timestamp order
, ncqStateFileSeq :: FileKey
, ncqStateVersion :: StateVersion
, ncqStateFacts :: Set Fact
}
deriving stock (Eq,Generic)
data NCQStorage3 =
NCQStorage3
{ ncqRoot :: FilePath
@ -74,13 +84,8 @@ data NCQStorage3 =
, ncqIdleThrsh :: Double
, ncqMMapCachedIdx :: TVar (HashPSQ FileKey CachePrio CachedIndex)
, ncqMMapCachedData :: TVar (HashPSQ FileKey CachePrio CachedData)
, ncqStateFiles :: TVar (HashSet FileKey)
, ncqStateIndex :: TVar [(Down POSIXTime, FileKey)] -- backward timestamp orde
, ncqStateFileSeq :: TVar FileKey
, ncqStateVersion :: TVar StateVersion
, ncqStateUsage :: TVar (IntMap (Int, HashSet FileKey))
, ncqStateFacts :: TVar (Set Fact)
, ncqMemTable :: Vector Shard
, ncqState :: TVar NCQState
, ncqWrites :: TVar Int
, ncqWriteEMA :: TVar Double -- for writes-per-seconds
, ncqWriteQ :: TVar (Seq HashRef)
@ -94,6 +99,82 @@ data NCQStorage3 =
}
instance Monoid FileKey where
mempty = FileKey 0
instance Semigroup FileKey where
(<>) (FileKey a) (FileKey b) = FileKey (max a b)
instance IsString FileKey where
fromString = FileKey . maybe maxBound fst . headMay . readHex . drop 1 . dropWhile (/= '-') . takeBaseName
instance ToFileName (DataFile FileKey) where
toFileName (DataFile fk) = ncqMakeFossilName fk
instance ToFileName (IndexFile FileKey) where
toFileName (IndexFile fk) = printf "i-%08x.cq" (coerce @_ @Word32 fk)
instance ToFileName (StateFile FileKey) where
toFileName (StateFile fk) = printf "s-%08x" (coerce @_ @Word32 fk)
instance Monoid NCQState where
mempty = ncqState0
instance Semigroup NCQState where
(<>) a b = NCQState files index seqq version facts
where
files = ncqStateFiles a <> ncqStateFiles b
index = ncqStateIndex a <> ncqStateIndex b
seqq = max (ncqStateFileSeq a) (ncqStateFileSeq b)
version = max (ncqStateVersion a) (ncqStateVersion b)
facts = ncqStateFacts a <> ncqStateFacts b
instance Pretty Location where
pretty = \case
InFossil k o s -> parens $ "in-fossil" <+> pretty k <+> pretty o <+> pretty s
InMemory _ -> "in-memory"
ncqMakeFossilName :: FileKey -> FilePath
ncqMakeFossilName = printf "f-%08x.data" . coerce @_ @Word32
ncqState0 :: NCQState
ncqState0 = NCQState{..}
where
ncqStateFiles = mempty
ncqStateIndex = mempty
ncqStateVersion = 0
ncqStateFacts = mempty
ncqStateFileSeq = 0
instance Pretty NCQState where
pretty NCQState{..} = vcat
[ prettyIndex
, prettyFiles
, prettyFacts
, prettySeq
]
where
prettySeq = "n" <+> pretty ncqStateFileSeq
prettyIndex = vcat
[ "i" <+> pretty fk <+> pretty (round @_ @Word64 p)
| (Down p, fk) <- ncqStateIndex
]
prettyFiles = vcat
[ "f" <+> pretty fk
| fk <- HS.toList ncqStateFiles
]
prettyFacts = vcat
[ pf f
| f <- Set.toList ncqStateFacts
]
pf (FI (DataFile a) (IndexFile b)) = "fi" <+> pretty a <+> pretty b
pf (P (PData (DataFile a) s)) = "fp" <+> pretty a <+> pretty s

View File

@ -14,6 +14,7 @@ import HBS2.Storage
import HBS2.Storage.Simple
import HBS2.Storage.Operations.ByteString
import HBS2.Storage.NCQ3
import HBS2.Storage.NCQ3.Internal.Files
import HBS2.System.Logger.Simple.ANSI
@ -28,6 +29,9 @@ import Data.Config.Suckless.System
import NCQTestCommon
import Data.ByteString qualified as BS
import Data.Ord
import Data.Set qualified as Set
import System.Random.MWC as MWC
import UnliftIO
@ -52,13 +56,24 @@ ncq3Tests = do
g <- liftIO MWC.createSystemRandom
runTest $ \TestEnv{..} -> do
ncqWithStorage3 testEnvDir $ \sto -> do
pending <- ncqWithStorage3 testEnvDir $ \sto -> do
notice $ "write" <+> pretty num <+> "blocks"
replicateM_ num do
n <- liftIO $ uniformRM (1024, 256*1024) g
bs <- liftIO $ genRandomBS g n
ncqPutBS sto (Just B) Nothing bs
fa <- readTVarIO (ncqState sto) <&> ncqStateFacts
pure $ [ (ncqGetFileName sto (toFileName k),s) | P (PData k s) <- Set.toList fa ]
& maximumByMay (comparing snd)
for_ pending $ \(dataFile,_) -> do
n <- liftIO $ uniformRM (1, 16*1024) g
bss <- liftIO $ genRandomBS g n
notice $ "CORRUPTING PENDING FILE" <+> pretty n <+> pretty dataFile
liftIO $ BS.appendFile dataFile bss
notice $ "reopen"
ncqWithStorage3 testEnvDir $ \sto -> do
pause @'Seconds 2