This commit is contained in:
voidlizard 2025-05-22 13:02:24 +03:00
parent fbb6c1730a
commit 3639ccec25
7 changed files with 400 additions and 113 deletions

View File

@ -186,3 +186,14 @@ It's just an easy way to create a such thing, you may browse it by hbs2 cat -H
_ -> throwIO (BadFormException @c nil) _ -> throwIO (BadFormException @c nil)
brief "shallow scan of a block/tree" $
entry $ bindMatch "hbs2:tree:scan:deep:stdout" $ nil_ \case
[HashLike href] -> do
sto <- getStorage
deepScan ScanDeep (const none) (coerce href) (getBlock sto) $ \ha -> do
liftIO $ print $ pretty ha
_ -> throwIO (BadFormException @c nil)

View File

@ -846,7 +846,7 @@ runPeer opts = respawnOnError opts $ flip runContT pure do
notice $ "run peer" <+> pretty (AsBase58 (view peerSignPk pc)) notice $ "run peer" <+> pretty (AsBase58 (view peerSignPk pc))
debug $ "STORAGE PREFIX" <+> pretty pref notice $ red "STORAGE PREFIX" <+> pretty pref
-- error "STOP" -- error "STOP"

View File

@ -170,6 +170,36 @@ data NCQStorage =
} }
-- Log structure:
-- (SD)*
-- S ::= word32be, section prefix
-- D ::= HASH PREFIX DATA
-- HASH ::= BYTESTRING(32)
-- PREFIX ::= BYTESTRING(4)
-- DATA ::= BYTESTRING(n) | n == S - LEN(WORD32) - LEN(HASH) - LEN(PREFIX)
newtype NCQFullRecordLen a =
NCQFullRecordLen a
deriving newtype (Num,Enum,Integral,Real,Ord,Eq)
-- including prefix
ncqFullDataLen :: forall a . Integral a => NCQFullRecordLen a -> a
ncqFullDataLen full = fromIntegral full - ncqKeyLen
{-# INLINE ncqFullDataLen #-}
ncqKeyLen :: forall a . Integral a => a
ncqKeyLen = 32
{-# INLINE ncqKeyLen #-}
-- 'S' in SD, i.e size, i.e section header
ncqSLen:: forall a . Integral a => a
ncqSLen = 4
{-# INLINE ncqSLen #-}
ncqDataOffset :: forall a b . (Integral a, Integral b) => a -> b
ncqDataOffset base = fromIntegral base + ncqSLen + ncqKeyLen
{-# INLINE ncqDataOffset #-}
instance MonadUnliftIO m => Storage NCQStorage HbSync LBS.ByteString m where instance MonadUnliftIO m => Storage NCQStorage HbSync LBS.ByteString m where
putBlock ncq lbs = fmap coerce <$> ncqStoragePutBlock ncq lbs putBlock ncq lbs = fmap coerce <$> ncqStoragePutBlock ncq lbs
enqueueBlock ncq lbs = fmap coerce <$> ncqStoragePutBlock ncq lbs enqueueBlock ncq lbs = fmap coerce <$> ncqStoragePutBlock ncq lbs
@ -412,6 +442,10 @@ ncqStorageRun ncq@NCQStorage{..} = flip runContT pure do
atomically $ modifyTVar ncqCurrentUsage (IntMap.adjust pred (fromIntegral fd)) atomically $ modifyTVar ncqCurrentUsage (IntMap.adjust pred (fromIntegral fd))
fdSeek fd AbsoluteSeek (fromIntegral $ 4 + 32 + off) fdSeek fd AbsoluteSeek (fromIntegral $ 4 + 32 + off)
bs <- Posix.fdRead fd (fromIntegral l) bs <- Posix.fdRead fd (fromIntegral l)
unless (BS.length bs == fromIntegral l) do
err $ "READ MISMATCH" <+> pretty l <+> pretty (BS.length bs)
atomically $ putTMVar answ bs atomically $ putTMVar answ bs
link reader link reader
@ -473,7 +507,7 @@ ncqStorageRun ncq@NCQStorage{..} = flip runContT pure do
what' <- race (pause @'Seconds 1) $ atomically do what' <- race (pause @'Seconds 1) $ atomically do
stop <- readTVar ncqStopped stop <- readTVar ncqStopped
q <- tryPeekTQueue indexQ q <- tryPeekTQueue indexQ
if not (stop || isJust q) then if not ( stop || isJust q) then
STM.retry STM.retry
else do else do
STM.flushTQueue indexQ STM.flushTQueue indexQ
@ -515,7 +549,6 @@ ncqStorageRun ncq@NCQStorage{..} = flip runContT pure do
Nothing -> pure mempty Nothing -> pure mempty
Just (h,_,WQItem{..},rest) -> do Just (h,_,WQItem{..},rest) -> do
off <- fdSeek fh SeekFromEnd 0
-- we really have to write tomb prefix here -- we really have to write tomb prefix here
let b = byteString (coerce @_ @ByteString h) let b = byteString (coerce @_ @ByteString h)
@ -524,7 +557,9 @@ ncqStorageRun ncq@NCQStorage{..} = flip runContT pure do
let wbs = toLazyByteString b let wbs = toLazyByteString b
let len = LBS.length wbs let len = LBS.length wbs
let ws = N.bytestring32 (fromIntegral len) let ws = N.bytestring32 (fromIntegral len)
let w = 4 + len let w = ncqSLen + len
off <- fdSeek fh SeekFromEnd 0
if isNothing wqData && wqNew then if isNothing wqData && wqNew then
pure () pure ()
@ -539,9 +574,10 @@ ncqStorageRun ncq@NCQStorage{..} = flip runContT pure do
fsize <- getFdStatus fh <&> PFS.fileSize fsize <- getFdStatus fh <&> PFS.fileSize
pure (0,fromIntegral fsize) pure (0,fromIntegral fsize)
now <- readTVarIO ncqIndexNow
if sz < ncqMinLog && now <= 0 then do -- off <- fdSeek fh SeekFromEnd 0 <&> subtract (fromIntegral w)
if sz < ncqMinLog then do
((h, (fromIntegral off, fromIntegral len)) : ) <$> next (written', rest) ((h, (fromIntegral off, fromIntegral len)) : ) <$> next (written', rest)
else do else do
pure [(h, (fromIntegral off, fromIntegral len))] pure [(h, (fromIntegral off, fromIntegral len))]
@ -563,14 +599,15 @@ ncqStorageRun ncq@NCQStorage{..} = flip runContT pure do
[] -> pure (q,w) [] -> pure (q,w)
((h,(o,l)):xs) -> do ((h,(o,l)):xs) -> do
modifyTVar wbytes (+l) modifyTVar wbytes (+l)
next (HPSQ.delete h q, HPSQ.insert h now1 (o,l) w,xs) let recLen = ncqFullDataLen (NCQFullRecordLen l)
next (HPSQ.delete h q, HPSQ.insert h now1 (o,recLen) w,xs)
writeTVar ncqWriteQueue rq writeTVar ncqWriteQueue rq
modifyTVar ncqStaged (IntMap.insert (fromIntegral fdr) rw) modifyTVar ncqStaged (IntMap.insert (fromIntegral fdr) rw)
bw <- readTVar wbytes bw <- readTVar wbytes
writeTVar ncqNotWritten (max 0 (b0 - bw)) writeTVar ncqNotWritten (max 0 (b0 - bw))
indexNow <- atomically $ stateTVar ncqIndexNow (,0) indexNow <- readTVarIO ncqIndexNow
when (fromIntegral size >= ncqMinLog || indexNow > 0) do when (fromIntegral size >= ncqMinLog || indexNow > 0) do
@ -598,6 +635,7 @@ ncqStorageRun ncq@NCQStorage{..} = flip runContT pure do
-- то есть должны отнять 1 после индексации. -- то есть должны отнять 1 после индексации.
modifyTVar ncqCurrentUsage (IntMap.insertWith (+) (fromIntegral fdr) 1) modifyTVar ncqCurrentUsage (IntMap.insertWith (+) (fromIntegral fdr) 1)
writeTQueue indexQ (fdr, fossilized) writeTQueue indexQ (fdr, fossilized)
writeTVar ncqIndexNow 0
closeFd fh closeFd fh
writeBinaryFileDurable (ncqGetCurrentSizeName ncq) (N.bytestring64 0) writeBinaryFileDurable (ncqGetCurrentSizeName ncq) (N.bytestring64 0)
@ -706,6 +744,15 @@ ncqStorageGetBlock ncq h = do
Just lbs | not (ncqIsTomb lbs) -> pure (Just $ LBS.drop ncqPrefixLen lbs) Just lbs | not (ncqIsTomb lbs) -> pure (Just $ LBS.drop ncqPrefixLen lbs)
_ -> pure Nothing _ -> pure Nothing
data NCQSectionType = B | R | T
deriving stock (Eq,Ord,Show)
instance Pretty NCQSectionType where
pretty = \case
B -> "B"
T -> "T"
R -> "R"
ncqPrefixLen :: Integral a => a ncqPrefixLen :: Integral a => a
ncqPrefixLen = 4 ncqPrefixLen = 4
{-# INLINE ncqPrefixLen #-} {-# INLINE ncqPrefixLen #-}
@ -832,20 +879,20 @@ ncqStorageScanDataFile ncq fp' action = do
flip runContT pure $ callCC \exit -> do flip runContT pure $ callCC \exit -> do
flip fix (0,mmaped) $ \next (o,bs) -> do flip fix (0,mmaped) $ \next (o,bs) -> do
when (BS.length bs < 4) $ exit () when (BS.length bs < ncqSLen) $ exit ()
let w = BS.take 4 bs & N.word32 & fromIntegral let w = BS.take ncqSLen bs & N.word32 & fromIntegral
when (BS.length bs < 4 + w) $ exit () when (BS.length bs < ncqSLen + w) $ exit ()
let kv = BS.drop 4 bs let kv = BS.drop ncqSLen bs
let k = BS.take 32 kv & coerce @_ @HashRef let k = BS.take ncqKeyLen kv & coerce @_ @HashRef
let v = BS.take (w-32) $ BS.drop 32 kv let v = BS.take (ncqFullDataLen (NCQFullRecordLen w)) $ BS.drop ncqKeyLen kv
lift (action o (fromIntegral w) k v) lift (action o (fromIntegral w) k v)
next (4 + o + fromIntegral w, BS.drop (w+4) bs) next (ncqSLen + o + fromIntegral w, BS.drop (w+ncqSLen) bs)
ncqStorageGet :: MonadUnliftIO m => NCQStorage -> HashRef -> m (Maybe LBS.ByteString) ncqStorageGet :: MonadUnliftIO m => NCQStorage -> HashRef -> m (Maybe LBS.ByteString)
ncqStorageGet ncq h = runMaybeT do ncqStorageGet ncq h = runMaybeT do
@ -869,7 +916,7 @@ ncqStorageGet_ ncq@NCQStorage{..} = \case
InFossil ce (o,l) -> do InFossil ce (o,l) -> do
now <- getTimeCoarse now <- getTimeCoarse
atomically $ writeTVar (cachedTs ce) now atomically $ writeTVar (cachedTs ce) now
let chunk = BS.take (fromIntegral l) (BS.drop (fromIntegral o + 4 + 32) (cachedMmapedData ce)) let chunk = BS.take (fromIntegral l) (BS.drop (ncqDataOffset o) (cachedMmapedData ce))
pure $ Just $ LBS.fromStrict chunk pure $ Just $ LBS.fromStrict chunk
_ -> pure Nothing _ -> pure Nothing
@ -884,7 +931,7 @@ ncqStorageGetRef ncq ref = runMaybeT do
lbs <- lift (ncqStorageGet ncq h) >>= toMPlus lbs <- lift (ncqStorageGet ncq h) >>= toMPlus
guard (not $ ncqIsTomb lbs) guard (not $ ncqIsTomb lbs)
let hbs = LBS.toStrict (LBS.drop ncqPrefixLen lbs) let hbs = LBS.toStrict (LBS.drop ncqPrefixLen lbs)
guard (BS.length hbs == 32) guard (BS.length hbs == ncqKeyLen)
pure $ coerce hbs pure $ coerce hbs
where h = ncqRefHash ncq ref where h = ncqRefHash ncq ref
@ -907,8 +954,9 @@ ncqStorageDel ncq@NCQStorage{..} h = flip runContT pure $ callCC \exit -> do
now <- getTimeCoarse now <- getTimeCoarse
let writeTombstone wq = do let writeTombstone wq = do
let recordPrefixLen = ncqSLen + ncqKeyLen + ncqPrefixLen
modifyTVar ncqWriteQueue (HPSQ.insert h now wq) modifyTVar ncqWriteQueue (HPSQ.insert h now wq)
modifyTVar ncqNotWritten (+ (4 + 32 + ncqPrefixLen)) modifyTVar ncqNotWritten (+ recordPrefixLen)
ncqLocate ncq h >>= atomically . \case ncqLocate ncq h >>= atomically . \case
Just (InFossil _ _) -> writeTombstone (WQItem False Nothing) Just (InFossil _ _) -> writeTombstone (WQItem False Nothing)
@ -1018,19 +1066,19 @@ ncqStorageOpen fp' = do
items <- S.toList_ <$> items <- S.toList_ <$>
flip runContT pure $ callCC \exit ->do flip runContT pure $ callCC \exit ->do
flip fix (0,bs0) $ \next (o,bs) -> do flip fix (0,bs0) $ \next (o,bs) -> do
when (BS.length bs < 4) $ exit () when (BS.length bs < ncqSLen) $ exit ()
let w = BS.take 4 bs & N.word32 & fromIntegral let w = BS.take ncqSLen bs & N.word32 & fromIntegral
let p = BS.take w (BS.drop 4 bs) let p = BS.take w (BS.drop ncqSLen bs)
when (BS.length p < w ) do when (BS.length p < w ) do
throwIO NCQStorageBrokenCurrent throwIO NCQStorageBrokenCurrent
let k = BS.take 32 p & coerce . BS.copy let k = BS.take ncqKeyLen p & coerce . BS.copy
let vs = w - 32 let vs = ncqFullDataLen (NCQFullRecordLen w)
lift $ S.yield (k,now, (fromIntegral o, fromIntegral vs)) lift $ S.yield (k,now, (fromIntegral o, fromIntegral vs))
next (o+w+4, BS.drop (w+4) bs) next (o+w+ncqSLen, BS.drop (w+ncqSLen) bs)
atomically $ modifyTVar ncqStaged (IntMap.insert (fromIntegral fd) (HPSQ.fromList items)) atomically $ modifyTVar ncqStaged (IntMap.insert (fromIntegral fd) (HPSQ.fromList items))
@ -1173,6 +1221,81 @@ ncqStorageInit_ check path = do
pure ncq pure ncq
data NCQFsckException =
NCQFsckException
deriving stock (Show,Typeable)
instance Exception NCQFsckException
data NCQFsckIssueType =
FsckInvalidPrefix
| FsckInvalidContent
| FsckInvalidFileSize
deriving stock (Eq,Ord,Show,Data,Generic)
data NCQFsckIssue =
NCQFsckIssue FilePath Word64 NCQFsckIssueType
deriving stock (Eq,Ord,Show,Data,Generic)
ncqFsck :: MonadUnliftIO m => FilePath -> m [NCQFsckIssue]
ncqFsck fp = do
isFile <- doesFileExist fp
if isFile then
ncqFsckOne fp
else do
fs <- dirFiles fp <&> List.filter ((== ".data") . takeExtension)
concat <$> mapM ncqFsckOne fs
ncqFsckOne :: MonadUnliftIO m => FilePath -> m [NCQFsckIssue]
ncqFsckOne fp = do
mmaped <- liftIO $ mmapFileByteString fp Nothing
toff <- newTVarIO 0
issuesQ <- newTQueueIO
let
emit :: forall m . MonadIO m => NCQFsckIssue -> m ()
emit = atomically . writeTQueue issuesQ
handle (\(_ :: ReadLogError) -> none) do
runConsumeBS mmaped do
readSections $ \size bs -> do
let ssz = LBS.length bs
let (hash, rest1) = LBS.splitAt 32 bs & over _1 (coerce . LBS.toStrict)
let (prefix, rest2) = LBS.splitAt ncqPrefixLen rest1 & over _1 LBS.toStrict
let (prefixOk,pt) = if | prefix == ncqBlockPrefix -> (True, Just B)
| prefix == ncqRefPrefix -> (True, Just R)
| prefix == ncqTombPrefix -> (True, Just T)
| otherwise -> (False, Nothing)
let contentOk = case pt of
Just B -> hash == hashObject @HbSync rest2
_ -> True
off <- readTVarIO toff
unless prefixOk $ emit (NCQFsckIssue fp off FsckInvalidPrefix)
unless contentOk $ emit (NCQFsckIssue fp off FsckInvalidContent)
liftIO $ atomically $ modifyTVar toff (\x -> x + 4 + fromIntegral (LBS.length bs))
debug $ pretty (takeFileName fp)
<+> pretty size
<+> pretty ssz
<+> brackets (pretty $ maybe "E" show pt)
<+> brackets (if contentOk then pretty hash else "invalid hash")
lastOff <- readTVarIO toff
unless (fromIntegral (BS.length mmaped) == lastOff) do
emit (NCQFsckIssue fp lastOff FsckInvalidFileSize)
atomically $ STM.flushTQueue issuesQ
ncqStorageFlush :: MonadUnliftIO m => NCQStorage -> m () ncqStorageFlush :: MonadUnliftIO m => NCQStorage -> m ()
ncqStorageFlush = ncqStorageSync ncqStorageFlush = ncqStorageSync

View File

@ -254,6 +254,17 @@ main = do
e -> throwIO $ BadFormException @C (mkList e) e -> throwIO $ BadFormException @C (mkList e)
entry $ bindMatch "ncq:fsck" $ nil_ \case
[ StringLike fpath ] -> lift do
issues <- ncqFsck fpath
for_ issues $ \i -> do
err $ viaShow i
unless (List.null issues) exitFailure
e -> throwIO $ BadFormException @C (mkList e)
entry $ bindMatch "ncq:cached:entries" $ \case entry $ bindMatch "ncq:cached:entries" $ \case
[ isOpaqueOf @TCQ -> Just tcq ] -> lift do [ isOpaqueOf @TCQ -> Just tcq ] -> lift do
NCQStorage{..} <- getNCQ tcq NCQStorage{..} <- getNCQ tcq

View File

@ -12,6 +12,7 @@ import HBS2.Hash
import HBS2.Data.Types.Refs import HBS2.Data.Types.Refs
import HBS2.Clock import HBS2.Clock
import HBS2.Merkle import HBS2.Merkle
import HBS2.Polling
import HBS2.Storage import HBS2.Storage
import HBS2.Storage.Simple import HBS2.Storage.Simple
@ -30,6 +31,7 @@ import Data.Config.Suckless.System
import DBPipe.SQLite hiding (field) import DBPipe.SQLite hiding (field)
import Data.Char
import Data.Bits import Data.Bits
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Data.ByteString qualified as BS import Data.ByteString qualified as BS
@ -174,6 +176,125 @@ testNCQFuckupRecovery1 TestEnv{..} = flip runContT pure do
notice $ "loaded" <+> pretty ha <+> pretty (LBS.length lbs) notice $ "loaded" <+> pretty ha <+> pretty (LBS.length lbs)
testNCQSimple1 :: MonadUnliftIO m => TestEnv -> m ()
testNCQSimple1 TestEnv{..} = flip runContT pure do
let ncqDir = testEnvDir </> "ncq-simple"
for_ [ 0 .. 18 ] $ \s -> do
let size = 2 ^ s
let payload = LBS.replicate size 0x41 -- 0x41 = 'A'
let expectedHash = hashObject @HbSync payload
-- Step 1: Write block
lift $ withNCQ id ncqDir $ \ncq -> do
let sto = AnyStorage ncq
h <- putBlock sto payload `orDie` "failed to write block"
liftIO $ assertBool "hashes match (write)" (h == expectedHash)
-- Step 2: Read back
lift $ withNCQ id ncqDir $ \ncq -> do
let sto = AnyStorage ncq
blk <- getBlock sto (coerce expectedHash) `orDie` "block not found"
sx <- hasBlock sto (coerce expectedHash)
loc <- ncqLocate ncq (coerce expectedHash)
>>= orThrowUser "not found"
blk0 <- ncqStorageGet_ ncq loc
let sblk0 = LBS.length <$> blk0
liftIO $ print $ "block size"
<+> pretty sx
<+> ";"
<+> pretty (LBS.length blk)
<+> ";"
<+> pretty size
<+> ";"
<+> pretty sblk0
<+> pretty loc
liftIO $ do
assertBool "block has correct length" (LBS.length blk == size)
assertBool "block contents are correct" (blk == payload)
testNCQSimple2 :: MonadUnliftIO m => Int -> TestEnv -> m ()
testNCQSimple2 n TestEnv{..} = flip runContT pure do
let ncqDir = testEnvDir </> "ncq-simple2"
let alph_ = V.fromList ['A' .. 'z']
cnt <- newTVarIO 0
let alphx = liftIO do
i <- atomically $ stateTVar cnt (\x -> (x, succ x))
pure $ alph_ ! ( i `mod` V.length alph_)
-- Step 1: write N blocks
hashes <- lift $ withNCQ id ncqDir $ \ncq -> do
let sto = AnyStorage ncq
replicateM n do
size <- liftIO $ randomRIO (0, 256 * 1024)
chr <- alphx
let payload = LBS.replicate size (fromIntegral $ ord chr)
let h = hashObject @HbSync payload
h' <- putBlock sto payload `orDie` "putBlock failed"
loc <- ncqLocate ncq (coerce h)
s <- hasBlock sto h
w <- getBlock sto h
let w' = fromMaybe mempty w
if w == Just payload then do
debug $ "okay" <+> pretty loc
else do
err $ pretty s <> "/" <> pretty size
<+> viaShow (LBS.take 48 w')
<+> ".."
<+> viaShow (LBS.take 8 $ LBS.reverse w')
<> line
<+> pretty loc
error "ABORTED"
liftIO $ assertBool "hash matches" (h == h')
pure (h, size, payload)
let testRead ncq = do
let sto = AnyStorage ncq
forM_ hashes $ \(h, expectedSize, expectedPayload) -> do
loc <- ncqLocate ncq (coerce h) >>= orThrowUser "not found"
blk <- getBlock sto (coerce h) `orDie` "block not found"
sx <- hasBlock sto (coerce h)
blk0 <- ncqStorageGet_ ncq loc
let sblk0 = LBS.length <$> blk0
let actualSize = LBS.length blk
debug $ "block size"
<+> pretty sx
<+> ";"
<+> pretty actualSize
<+> ";"
<+> pretty expectedSize
<+> ";"
<+> pretty sblk0
<+> pretty loc
liftIO do
assertBool "size match" (actualSize == expectedSize)
assertBool "payload match" (blk == expectedPayload)
-- Step 2: reopen and verify
lift $ withNCQ id ncqDir $ \ncq -> do
testRead ncq
-- ncqIndexRightNow ncq
pause @'Seconds 2
liftIO $ print $ "LAST PASS"
-- Step 3: reopen and verify - fossil
lift $ withNCQ id ncqDir $ \ncq -> do
testRead ncq
testNCQ1 :: MonadUnliftIO m testNCQ1 :: MonadUnliftIO m
=> Int => Int
-> TestEnv -> TestEnv
@ -196,109 +317,113 @@ testNCQ1 n TestEnv{..} = flip runContT pure do
let fname = inputDir </> show i <> ".bin" let fname = inputDir </> show i <> ".bin"
size <- randomRIO (1, 256*1024) size <- randomRIO (1, 256*1024)
atomically $ modifyTVar nSize (+size) atomically $ modifyTVar nSize (+size)
file <- LBS.take size <$> LBS.readFile "/dev/urandom" file <- LBS.toStrict . LBS.take size <$> LBS.readFile "/dev/urandom"
BS.writeFile fname (BS.toStrict file) BS.writeFile fname file
pure fname let ha = hashObject @HbSync file
pure (fname, (ha, fromIntegral $ BS.length file))
ncq <- liftIO $ ncqStorageOpen ncqDir
r <- liftIO $ async (ncqStorageRun ncq) liftIO do
withNCQ id ncqDir $ \ncq -> flip runContT pure do
let sto = AnyStorage ncq
let fileMap = HM.fromList [ (ha,(s,fn)) | (fn,(ha,s)) <- fss ]
let
written :: forall m a . (Fractional a, MonadIO m) => m [(HashRef, a)]
written = readTVarIO twritten <&> HS.toList <&> fmap (,0.1)
ContT $ withAsync $ forever do
polling (Polling 0.25 0.25) written $ \(HashRef hz) -> liftIO $ void $ asyncLinked do
what <- getBlock sto hz >>= orThrowUser ("block not found" <+> pretty hz)
let h2 = hashObject @HbSync what
(s,_) <- HM.lookup hz fileMap & orThrowUser "fileMap entry missed"
ssz <- hasBlock sto hz
>>= orThrowUser ("block size not found" <+> pretty hz)
when (ssz /= s) do
error $ show $ "size mismatch" <+> pretty hz
when (hz /= h2) do
error $ show $ pretty "hash does not match" <+> pretty hz <+> pretty s
liftIO $ forConcurrently_ fss $ \(fn, (ha,s)) -> do
co <- liftIO (BS.readFile fn) <&> LBS.fromStrict
h1 <- putBlock sto co >>= orThrowUser "block not written"
lbs2 <- getBlock sto ha >>= orThrowUser "block not found"
let h2 = hashObject @HbSync lbs2
when (ha /= h2 || h1 /= ha) do
error $ show $ pretty "hash does not match" <+> pretty h1 <+> pretty s
atomically $ modifyTVar twritten (HS.insert (HashRef h1))
debug $ "putBlock" <+> pretty ha <+> pretty h2
liftIO $ forConcurrently_ fss $ \(fn, (ha,s)) -> do
lbs2 <- getBlock sto ha >>= orThrowUser "block not found"
let h2 = hashObject @HbSync lbs2
when (ha /= h2) do
error $ show $ pretty "hash does not match" <+> pretty ha <+> pretty s
debug $ "getBlock" <+> pretty ha <+> pretty h2
liftIO do
withNCQ id ncqDir $ \ncq -> flip runContT pure do
let sto = AnyStorage ncq let sto = AnyStorage ncq
nWq <- newTVarIO 0 for_ fss $ \(fn, (ha,s)) -> do
nCu <- newTVarIO 0 lbs2 <- getBlock sto ha >>= orThrowUser "block not found"
nFo <- newTVarIO 0 let h2 = hashObject @HbSync lbs2
nMissed <- newTVarIO 0
let when (ha /= h2) do
updateStats :: forall m . MonadIO m => NCQStorage -> HashRef -> m (Maybe Location) error $ show $ pretty "hash does not match" <+> pretty ha <+> pretty s
updateStats ncq h = do
w <- ncqLocate ncq (coerce h)
case w of debug $ "getBlock" <+> pretty ha <+> pretty h2
Just (InWriteQueue _) -> atomically $ modifyTVar nWq succ
Just (InCurrent _) -> atomically $ modifyTVar nCu succ
Just (InFossil _ _) -> atomically $ modifyTVar nFo succ
Nothing -> atomically $ modifyTVar nMissed succ
pure w
t1 <- ContT $ withAsync $ fix \loop -> do testNCQTree1 :: MonadUnliftIO m
=> Int
-> TestEnv
-> m ()
what <- readTVarIO twritten testNCQTree1 n TestEnv{..} = flip runContT pure do
p <- randomRIO (0.01, 0.5)
pause @'Seconds (realToFrac p)
forConcurrently_ what $ \h -> do let size = 1024 * 1024 * fromIntegral n
w <- updateStats ncq h let tmp = testEnvDir
what <- ncqStorageHasBlockEither ncq (coerce h) let inputDir = tmp </> "input"
case what of let ncqDir = tmp </> "ncq-test-data"
Left LocationNotFound | isJust w -> do
error $ show $ "FUCKING RACE!" <+> pretty w
Left e -> throwIO e treeLbs <- LBS.take size <$> liftIO (LBS.readFile ("/dev/urandom"))
Right _ -> none
done <- readTVarIO (ncqStopped ncq) let h1 = hashObject @HbSync treeLbs
unless done loop
link t1
--
out <- newTQueueIO
liftIO do
forConcurrently_ fss $ \f -> do
-- debug $ "process file" <+> pretty f
blk <- BS.readFile f
h <- putBlock sto (LBS.fromStrict blk) `orDie` ("Can't store block " <> f)
atomically do
writeTQueue out (HashRef h)
modifyTVar twritten (HS.insert (coerce h))
blkQ <- atomically do
STM.flushTQueue out
notice $ "WAIT BLOCKS DONE" <+> pretty (List.length blkQ)
lift $ ncqStorageFlush ncq
for_ blkQ $ \h -> liftIO do
void $ updateStats ncq h
hasBlock sto (coerce h)
`orDie` show ("missed" <+> pretty h)
liftIO $ ncqStorageStop ncq
wait t1
let vars = zip [ "write-q"
, "current"
, "fossil"
, "missed"
, "size"
]
[nWq, nCu, nFo, nMissed, nSize]
liftIO $ wait r
lift $ withNCQ id ncqDir $ \ncq1 -> do lift $ withNCQ id ncqDir $ \ncq1 -> do
for_ blkQ $ \h -> liftIO do
void $ updateStats ncq1 h
hasBlock (AnyStorage ncq1) (coerce h) >>= \case
Nothing -> print $ "missed" <+> pretty h
Just x -> none
results <- for vars $ \(k,w) -> do let sto = AnyStorage ncq1
v <- readTVarIO w
pure $ mkList @C [ mkSym k, mkInt v]
liftIO $ print $ pretty $ mkList (mkSym "results" : results) r <- createTreeWithMetadata sto Nothing mempty treeLbs
>>= orThrowPassIO
lbs2 <- runExceptT (getTreeContents sto r)
>>= orThrowPassIO
let h2 = hashObject @HbSync lbs2
let l1 = LBS.length treeLbs
let l2 = LBS.length treeLbs
display (mkList @C [mkSym r, mkSym h1, mkSym h2, mkInt l1, mkInt l2])
liftIO $ assertBool "hashes equal" (h1 == h2)
-- display (mkSym @C $ show $ pretty r)
testNCQRefs1 :: MonadUnliftIO m testNCQRefs1 :: MonadUnliftIO m
=> Int => Int
@ -404,6 +529,14 @@ main = do
debug $ "test:ncq:fuckup-recovery1" debug $ "test:ncq:fuckup-recovery1"
runTest testNCQFuckupRecovery1 runTest testNCQFuckupRecovery1
entry $ bindMatch "test:ncq:test-simple1" $ nil_ $ \case
[] -> runTest $ testNCQSimple1
e -> throwIO $ BadFormException @C (mkList e)
entry $ bindMatch "test:ncq:test-simple2" $ nil_ $ \case
[ LitIntVal n ] -> runTest $ testNCQSimple2 (fromIntegral n)
e -> throwIO $ BadFormException @C (mkList e)
entry $ bindMatch "test:ncq:test1" $ nil_ $ \case entry $ bindMatch "test:ncq:test1" $ nil_ $ \case
[ LitIntVal n ] -> do [ LitIntVal n ] -> do
debug $ "ncq:test1" <+> pretty n debug $ "ncq:test1" <+> pretty n
@ -418,6 +551,12 @@ main = do
e -> throwIO $ BadFormException @C (mkList e) e -> throwIO $ BadFormException @C (mkList e)
entry $ bindMatch "test:ncq:tree1" $ nil_ $ \case
[ LitIntVal n ] -> do
debug $ "ncq:tree1" <+> pretty n
runTest $ testNCQTree1 (fromIntegral n)
e -> throwIO $ BadFormException @C (mkList e)
entry $ bindMatch "test:ncq:test-lock" $ nil_ $ \case entry $ bindMatch "test:ncq:test-lock" $ nil_ $ \case
[ ] -> do [ ] -> do

View File

@ -1070,7 +1070,7 @@ bindCliArgs :: forall c m . (IsContext c, MonadUnliftIO m, Exception (BadFormExc
bindCliArgs a = do bindCliArgs a = do
bind "$*" (mkList a) bind "$*" (mkList a)
bind "*args" (mkList a) bind "*args" (mkList a)
forM_ (zip [0..] a) $ \(i,e) -> do forM_ (zip [1..] a) $ \(i,e) -> do
bind (fromString ("$"<>show i)) e bind (fromString ("$"<>show i)) e
internalEntries :: forall c m . ( IsContext c internalEntries :: forall c m . ( IsContext c

View File

@ -365,6 +365,9 @@ instance MkId (String,Integer) where
instance IsContext c => MkSym c Id where instance IsContext c => MkSym c Id where
mkSym = Symbol noContext mkSym = Symbol noContext
instance {-# OVERLAPPABLE #-} (IsContext c, Pretty a) => MkSym c a where
mkSym a = Symbol noContext (mkId (show $ pretty a))
class IsContext c => MkStr c s where class IsContext c => MkStr c s where
mkStr :: s -> Syntax c mkStr :: s -> Syntax c