hbs2-peer migrate from NCQv1, test

This commit is contained in:
voidlizard 2025-08-21 13:36:14 +03:00
parent 513f03eeb3
commit 789c798e7e
10 changed files with 465 additions and 356 deletions

View File

@ -4,69 +4,20 @@ module HBS2.CLI.NCQ3.Migrate where
import HBS2.CLI.Prelude
import HBS2.CLI.Run.Internal
import HBS2.Base58
import HBS2.Data.Types.Refs
import HBS2.Hash
import HBS2.Peer.NCQ3.Migrate.NCQ
import HBS2.Net.Auth.Schema()
import HBS2.Peer.CLI.Detect
import HBS2.Peer.RPC.API.LWWRef
import HBS2.Peer.RPC.API.Peer
import HBS2.Peer.RPC.API.RefLog
import HBS2.Peer.RPC.API.Storage
import HBS2.Peer.RPC.Client
import HBS2.Peer.RPC.Client.Unix
import HBS2.Storage
import HBS2.Storage.NCQ3
import HBS2.Storage.NCQ3.Internal.Files
import HBS2.Storage.NCQ3.Internal.Fossil
import HBS2.Storage.NCQ3.Internal.Index
import HBS2.Storage.NCQ3.Internal.Prelude
import HBS2.Peer.Proto.RefLog
import HBS2.Peer.Proto.RefChan
import HBS2.Peer.Proto.LWWRef
import Data.Config.Suckless.System
import Data.ByteString.Lazy qualified as LBS
import Data.ByteString qualified as BS
import Control.Monad.Trans.Cont
import Control.Monad.Trans.Maybe
import Data.HashSet (HashSet)
import Data.HashSet qualified as HS
import Data.HashMap.Strict qualified as HM
import Data.HashMap.Strict (HashMap)
import Data.Either
import Data.List qualified as L
import System.Directory (getModificationTime)
import UnliftIO.IO
import Streaming.Prelude qualified as S
{- HLINT ignore "Functor law"-}
data SomeRef = forall a . ( Eq a
, Hashable a
, Hashed HbSync a
, Pretty a
, RefMetaData a
)
=> SomeRef a
instance RefMetaData SomeRef where
refMetaData (SomeRef a) = refMetaData a
instance Pretty SomeRef where
pretty (SomeRef wtf) = pretty wtf
instance Hashed HbSync SomeRef where
hashObject (SomeRef a) = hashObject a
legacyHashRefNCQ1 :: HashRef -> HashRef -> HashRef
legacyHashRefNCQ1 salt h = HashRef (hashObject (coerce @_ @ByteString h <> coerce salt))
legacyHashRefFor :: Hashed HbSync p => HashRef -> p -> HashRef
legacyHashRefFor salt x = legacyHashRefNCQ1 salt (HashRef $ hashObject @HbSync $ x)
migrateEntries :: forall c m . ( MonadUnliftIO m
, IsContext c
, Exception (BadFormException c)
@ -92,9 +43,9 @@ migrateEntries = do
<&> fromMaybe mempty
rrefs <- S.toList_ <$> for refs $ \(pk, s, _) -> case s of
"reflog" -> S.yield (SomeRef $ RefLogKey @'HBS2Basic pk)
"refchan" -> S.yield (SomeRef $ RefChanLogKey @'HBS2Basic pk)
"lwwref" -> S.yield (SomeRef $ LWWRefKey @'HBS2Basic pk)
"reflog" -> S.yield (WrapRef $ RefLogKey @'HBS2Basic pk)
"refchan" -> S.yield (WrapRef $ RefChanLogKey @'HBS2Basic pk)
"lwwref" -> S.yield (WrapRef $ LWWRefKey @'HBS2Basic pk)
_ -> none
lift $ migrateNCQ1 nicelog rrefs src dst
@ -104,90 +55,3 @@ migrateEntries = do
nicelog :: forall m . MonadIO m => Doc AnsiStyle -> m ()
nicelog doc = liftIO $ hPutDoc stdout (doc <> line)
migrateNCQ1 :: MonadUnliftIO m
=> ( Doc AnsiStyle -> IO () )
-> [SomeRef]
-> FilePath
-> FilePath
-> m ()
migrateNCQ1 logger refs src b = flip runContT pure do
debug "migrate NCQ to NCQ3"
let a0 = src </> "0"
a0here <- doesPathExist a0
let a = if a0here then a0 else src
ncq <- ContT $ ncqWithStorage b
let sto = AnyStorage ncq
let mlog = ncqGetFileName ncq "migrate-ncq1.log"
info $ "set SOURCE dir" <+> pretty a
ncqSalt <- liftIO (try @_ @IOException (BS.readFile (src </> ".seed")))
<&> fromRight mempty
<&> HashRef . hashObject
notice $ "ref. salt" <+> pretty ncqSalt
files <- do
fs <- dirFiles a <&> L.filter (\x -> takeExtension x == ".data")
mtimes <- liftIO $ mapM getModificationTime fs
pure [ f | (f,_) <- L.sortOn snd (zip fs mtimes) ]
touch mlog
processed <- HS.fromList . lines <$> liftIO (readFile mlog)
let refz = HM.fromList [ (legacyHashRefFor ncqSalt r, SomeRef r) | SomeRef r <- refs ]
rv <- newTVarIO ( mempty :: HashMap HashRef (SomeRef, HashRef) )
nblk <- newTVarIO 0
for_ files $ \f -> void $ runMaybeT do
guard ( not $ HS.member f processed )
debug $ "processing" <+> pretty f
ncqStorageScanDataFile0 f $ \offset w key s -> void $ runMaybeT do
(t, bs) <- toMPlus (ncqEntryUnwrapValue s)
case t of
B -> do
h1 <- putBlock sto (LBS.fromStrict bs)
atomically $ modifyTVar nblk succ
R -> do
case HM.lookup key refz of
Nothing -> none
Just r -> do
atomically $ modifyTVar rv (HM.insert (HashRef $ hashObject r) (r, HashRef (coerce bs)))
_ -> none
liftIO $ appendFile mlog (f <> "\n")
nb <- readTVarIO nblk
liftIO $ logger $ "moved" <+> pretty nb <+> "blocks"
foundRefs <- readTVarIO rv <&> HM.toList
for_ foundRefs $ \(_, (r, v)) -> do
blk <- hasBlock sto (coerce v)
when (isJust blk) do
liftIO $ logger $ green "update ref" <+> pretty r <+> pretty v
updateRef sto r (coerce v)
-- for_ refs $ \(SomeRef r) -> void $ runMaybeT do
-- v <- getRef origSto r >>= toMPlus
-- v0 <- hasBlock origSto v
-- v1 <- hasBlock sto v
-- updateRef sto r v
-- v3 <- getRef sto r
-- let okay = if v3 == Just v then green "ok" else red "fail"
-- liftIO $ logger $ okay <+> "update ref" <+> pretty r <+> pretty v <+> pretty v1 <+> parens (pretty v0)

View File

@ -1,14 +1,25 @@
module Migrate where
import HBS2.Prelude.Plated
import HBS2.Clock
import HBS2.Misc.PrettyStuff
import HBS2.Hash
import HBS2.Data.Types.Refs
import HBS2.OrDie
import HBS2.Defaults
import HBS2.Storage.NCQ
import HBS2.Storage
import HBS2.Storage.NCQ3
import HBS2.Peer.Proto.RefLog
import HBS2.Peer.Proto.RefChan
import HBS2.Peer.Proto.LWWRef
import HBS2.Net.Proto.Types
import Log
import PeerConfig
import Brains
import HBS2.Peer.NCQ3.Migrate.NCQ qualified as N
import HBS2.Peer.NCQ3.Migrate.NCQ (WrapRef(..))
import Data.Config.Suckless.Script hiding (optional)
import Data.Config.Suckless.Script.File (glob)
@ -26,262 +37,330 @@ import System.Exit
import Data.ByteString qualified as BS
import Data.ByteString.Lazy qualified as LBS
import Control.Exception
import Control.Monad.Cont
import Control.Monad.Trans.Cont
import Control.Monad.Reader
import UnliftIO
-- import UnliftIO.Temporary
import Streaming.Prelude qualified as S
migrate :: [Syntax C]-> IO ()
migrate syn = flip runContT pure $ callCC \exit -> do
xdg <- liftIO $ getXdgDirectory XdgData defStorePath <&> fromString
let (opts, argz) = splitOpts [ ("-n",0)
, ("--dry",0)
, ("--no-refs",0)
, ("--help",0)
let (opts, argz) = splitOpts [ ("-c",1)
-- , ("--dry",0)
-- , ("--no-refs",0)
-- , ("--help",0)
] syn
prefix <- headMay [ p | StringLike p <- argz ]
& orThrowUser ( "Storage dir not specified" <+> parens ("typically" <+> pretty xdg) <> line
<> line
<> "run hbs2-peer migrate" <+> pretty xdg <> line
<> "if this is it"
)
-- FIXME: migrate-simple-storage!
-- KISS. just import block/remove block.
-- let the user backup it.
--
--
--
conf@(PeerConfig se) <- peerConfigRead (headMay [p | ListVal [StringLike "-c", StringLike p] <- opts] )
let dry = or [ True | ListVal [StringLike s] <- opts, s `elem` ["--dry","-n"]]
brains <- newBasicBrains conf
let norefs = or [ True | ListVal [StringLike "--no-refs"] <- opts ]
bProbe <- newSimpleProbe "Brains"
brainsThread <- ContT $ withAsync $ runBasicBrains conf brains
xdg <- liftIO $ getXdgDirectory XdgData defStorePath <&> fromString
let prefix = fromMaybe xdg $ runReader (cfgValue @PeerStorageKey @(Maybe FilePath)) se
let store = prefix
let migrateDir = store </> "migrate"
let ncqDir = store </> "ncq"
let ncqDirBackup = store </> ".ncq.backup"
let ncq3Dir = store </> "ncq3"
liftIO $ IO.hSetBuffering stdin NoBuffering
liftIO $ IO.hSetBuffering stdout LineBuffering
ncqHere <- Sy.doesDirectoryExist ncqDir
already <- Sy.doesDirectoryExist migrateDir
unless ncqHere $ exit ()
when already do
liftIO $ hPutDoc stdout $ yellow "Found migration WIP" <+> pretty migrateDir <> "," <+> "continue" <> line
liftIO $ hPrint stderr $
"Migrate" <+> pretty ncqDir <> line
<> "you may remove" <+> pretty ncqDir
<+> "when migration successfully done"
<+> "or you may back it up" <> line
liftIO $ hPutDoc stdout $
yellow "Storage migration process is about to start" <> line
<> "It will convert the current storage structure to a new one (NCQ storage)" <> line
<> "to use with hbs2 0.25.2 and newer" <> line
<> "hbs2-peer 0.25.1 and earlier versions don't work with the new storage." <> line
<> "If you want to backup your data first just for in case" <> line
<> "You may store the contents of directory" <> line
<> line
<> pretty prefix
<> line <> line
<> "specifically" <+> pretty (prefix </> "blocks") <+> "and" <+> pretty (prefix</> "refs")
<> line
<> "to roll back to the older version --- just restore them"
<> line
flip fix 10 \next i -> do
liftIO $ hPrint stderr $ pretty i <> "..."
pause @'Seconds 1
when (i > 0) $ next (pred i)
liftIO do
IO.hFlush stdout
IO.hFlush stderr
putStr "Start the migration process? [y]: "
IO.hFlush stdout
notice "Go!"
y <- liftIO getChar
notice "Seek for polled references"
unless ( toUpper y == 'Y' ) $ exit ()
refs <- listPolledRefs @L4Proto brains Nothing
liftIO do
putStrLn ""
rrefs <- S.toList_ <$> for refs $ \(pk, s, _) -> case s of
"reflog" -> S.yield (WrapRef $ RefLogKey @'HBS2Basic pk)
"refchan" -> S.yield (WrapRef $ RefChanLogKey @'HBS2Basic pk)
"lwwref" -> S.yield (WrapRef $ LWWRefKey @'HBS2Basic pk)
_ -> none
info $ "migration started" <+> pretty opts
info $ "create dir" <+> pretty migrateDir
notice $ "got references" <+> vcat (pretty <$> rrefs)
mkdir migrateDir
lift $ N.migrateNCQ1 notice rrefs ncqDir ncq3Dir
wip <- Sy.doesDirectoryExist migrateDir
notice $ "move" <+> pretty ncqDir <+> pretty ncqDirBackup
<> line <> "you may remove it if you want"
source <- if dry && not wip then do
pure store
else do
info $ yellow "Real migration," <+> "fasten the sit belts!"
let srcDir = migrateDir </> "source"
mkdir srcDir
let inBlk = store </> "blocks"
let inRefs = store </> "refs"
mv ncqDir ncqDirBackup
e1 <- Sy.doesDirectoryExist inBlk
when e1 $ mv inBlk (srcDir </> "blocks")
-- let (opts, argz) = splitOpts [ ("-n",0)
-- , ("--dry",0)
-- , ("--no-refs",0)
-- , ("--help",0)
-- ] syn
e2 <- Sy.doesDirectoryExist inRefs
-- prefix <- headMay [ p | StringLike p <- argz ]
-- & orThrowUser ( "Storage dir not specified" <+> parens ("typically" <+> pretty xdg) <> line
-- <> line
-- <> "run hbs2-peer migrate" <+> pretty xdg <> line
-- <> "if this is it"
-- )
when e2 $ mv inRefs (srcDir </> "refs")
pure srcDir
-- let dry = or [ True | ListVal [StringLike s] <- opts, s `elem` ["--dry","-n"]]
tmp <- ContT $ Temp.withTempDirectory migrateDir "run"
-- let norefs = or [ True | ListVal [StringLike "--no-refs"] <- opts ]
info $ "create dir" <+> pretty tmp
let blkz = source </> "blocks"
let refz = source </> "refs"
-- let store = prefix
let b = tmp </> "blocks"
let r = tmp </> "refs"
-- let migrateDir = store </> "migrate"
-- let ncqDir = store </> "ncq"
info $ "create directory links"
info $ pretty blkz <+> pretty b
liftIO $ createDirectoryLink blkz b
-- liftIO $ IO.hSetBuffering stdin NoBuffering
-- liftIO $ IO.hSetBuffering stdout LineBuffering
info $ pretty refz <+> pretty r
liftIO $ createDirectoryLink refz r
-- already <- Sy.doesDirectoryExist migrateDir
ncq <- ContT $ withNCQ id ncqDir
-- when already do
-- liftIO $ hPutDoc stdout $ yellow "Found migration WIP" <+> pretty migrateDir <> "," <+> "continue" <> line
let nameToHash fn =
fromString @HashRef $ mconcat $ reverse $ take 2 $ reverse $ splitDirectories fn
-- liftIO $ hPutDoc stdout $
-- yellow "Storage migration process is about to start" <> line
-- <> "It will convert the current storage structure to a new one (NCQ storage)" <> line
-- <> "to use with hbs2 0.25.2 and newer" <> line
-- <> "hbs2-peer 0.25.1 and earlier versions don't work with the new storage." <> line
-- <> "If you want to backup your data first just for in case" <> line
-- <> "You may store the contents of directory" <> line
-- <> line
-- <> pretty prefix
-- <> line <> line
-- <> "specifically" <+> pretty (prefix </> "blocks") <+> "and" <+> pretty (prefix</> "refs")
-- <> line
-- <> "to roll back to the older version --- just restore them"
-- <> line
let hashToPath ha = do
let (p,r) = splitAt 1 (show $ pretty ha)
p </> r
-- liftIO do
-- IO.hFlush stdout
-- IO.hFlush stderr
-- putStr "Start the migration process? [y]: "
-- IO.hFlush stdout
checkQ <- newTQueueIO
checkN <- newTVarIO 0
-- y <- liftIO getChar
errors <- newTVarIO 0
-- unless ( toUpper y == 'Y' ) $ exit ()
rmp <- liftIO $ async $ fix \next -> do
atomically (readTQueue checkQ) >>= \case
Nothing -> none
Just what -> do
-- liftIO do
-- putStrLn ""
toWipe <- ncqLocate ncq what >>= \case
Just (InCurrent{}) -> do
atomically $ modifyTVar checkN pred
pure True
-- info $ "migration started" <+> pretty opts
Just (InFossil{}) -> do
atomically $ modifyTVar checkN pred
pure True
-- info $ "create dir" <+> pretty migrateDir
Just (InWriteQueue{}) -> do
atomically $ unGetTQueue checkQ (Just what)
pure False
-- mkdir migrateDir
Nothing -> do
atomically $ modifyTVar errors succ
pure False
-- wip <- Sy.doesDirectoryExist migrateDir
when toWipe do
let path = b </> hashToPath what
info $ yellow "d" <+> pretty what
-- source <- if dry && not wip then do
-- pure store
-- else do
-- info $ yellow "Real migration," <+> "fasten the sit belts!"
-- let srcDir = migrateDir </> "source"
-- mkdir srcDir
-- let inBlk = store </> "blocks"
-- let inRefs = store </> "refs"
unless dry do
rm path
-- e1 <- Sy.doesDirectoryExist inBlk
next
-- when e1 $ mv inBlk (srcDir </> "blocks")
cnt <- newTVarIO 0
-- e2 <- Sy.doesDirectoryExist inRefs
glob ["**/*"] [] b $ \fn -> flip runContT pure $ callCC \next -> do
sz <- liftIO $ getFileSize fn
-- when e2 $ mv inRefs (srcDir </> "refs")
when (sz >= 1024^3 ) do
err $ red "Block is too large; skipping" <+> pretty fn
next True
-- pure srcDir
when (sz >= 1024^2 ) do
warn $ yellow "Block is too large; but okay" <+> pretty fn
-- tmp <- ContT $ Temp.withTempDirectory migrateDir "run"
let hs = nameToHash fn
-- info $ "create dir" <+> pretty tmp
bs <- liftIO $ BS.copy <$> BS.readFile fn
let h = HashRef $ hashObject @HbSync bs
-- let blkz = source </> "blocks"
-- let refz = source </> "refs"
unless ( h == hs ) do
err $ red "Hash doesn't match content" <+> pretty fn
next True
-- let b = tmp </> "blocks"
-- let r = tmp </> "refs"
placed <- liftIO $ ncqStoragePutBlock ncq (LBS.fromStrict bs)
-- info $ "create directory links"
-- info $ pretty blkz <+> pretty b
-- liftIO $ createDirectoryLink blkz b
flush <- atomically do
n <- readTVar cnt
if n > 1000 then do
writeTVar cnt 0
pure True
else do
modifyTVar cnt succ
pure False
-- info $ pretty refz <+> pretty r
-- liftIO $ createDirectoryLink refz r
unless ( placed == Just hs ) do
err $ red "NCQ write error" <+> pretty fn
next True
-- ncq <- ContT $ withNCQ id ncqDir
when flush do
liftIO (ncqStorageFlush ncq)
-- let nameToHash fn =
-- fromString @HashRef $ mconcat $ reverse $ take 2 $ reverse $ splitDirectories fn
for_ placed $ \hx -> atomically do
writeTQueue checkQ (Just hx)
modifyTVar checkN succ
-- let hashToPath ha = do
-- let (p,r) = splitAt 1 (show $ pretty ha)
-- p </> r
info $ green "ok" <+> "B" <+> fill 44 (pretty placed) <+> pretty sz
-- checkQ <- newTQueueIO
-- checkN <- newTVarIO 0
pure True
-- errors <- newTVarIO 0
unless norefs do
glob ["**/*"] [] r $ \fn -> flip runContT pure $ callCC \next -> do
-- rmp <- liftIO $ async $ fix \next -> do
-- atomically (readTQueue checkQ) >>= \case
-- Nothing -> none
-- Just what -> do
let ref = nameToHash fn
-- toWipe <- ncqLocate ncq what >>= \case
-- Just (InCurrent{}) -> do
-- atomically $ modifyTVar checkN pred
-- pure True
ncqRef <- liftIO $ ncqStorageGetRef ncq ref
-- Just (InFossil{}) -> do
-- atomically $ modifyTVar checkN pred
-- pure True
when (isJust ncqRef) do
info $ yellow "keep" <+> "R" <+> pretty ref
next True
-- Just (InWriteQueue{}) -> do
-- atomically $ unGetTQueue checkQ (Just what)
-- pure False
refTo <- liftIO (readFile fn)
<&> coerce @_ @HashRef . fromString @(Hash HbSync)
-- Nothing -> do
-- atomically $ modifyTVar errors succ
-- pure False
here <- liftIO (ncqLocate ncq refTo)
-- when toWipe do
-- let path = b </> hashToPath what
-- info $ yellow "d" <+> pretty what
if isJust here then do
liftIO $ ncqStorageSetRef ncq ref refTo
info $ green "ok" <+> "R" <+> pretty ref <+> pretty refTo
else do
warn $ red "Missed block for ref" <+> pretty ref <+> pretty refTo
-- unless dry do
-- rm path
pure True
-- next
liftIO $ ncqIndexRightNow ncq
-- cnt <- newTVarIO 0
info $ "check migration / wait to complete"
-- glob ["**/*"] [] b $ \fn -> flip runContT pure $ callCC \next -> do
-- sz <- liftIO $ getFileSize fn
atomically $ writeTQueue checkQ Nothing
-- when (sz >= 1024^3 ) do
-- err $ red "Block is too large; skipping" <+> pretty fn
-- next True
wait rmp
-- when (sz >= 1024^2 ) do
-- warn $ yellow "Block is too large; but okay" <+> pretty fn
num <- readTVarIO checkN
-- let hs = nameToHash fn
when (num == 0) $ exit ()
-- bs <- liftIO $ BS.copy <$> BS.readFile fn
-- let h = HashRef $ hashObject @HbSync bs
ee <- readTVarIO errors
rest <- readTVarIO checkN
-- unless ( h == hs ) do
-- err $ red "Hash doesn't match content" <+> pretty fn
-- next True
liftIO $ hPutDoc stdout $ "errors" <+> pretty ee <+> "leftovers" <+> pretty rest
-- placed <- liftIO $ ncqStoragePutBlock ncq (LBS.fromStrict bs)
liftIO do
if ee == 0 && rest == 0 then do
-- flush <- atomically do
-- n <- readTVar cnt
-- if n > 1000 then do
-- writeTVar cnt 0
-- pure True
-- else do
-- modifyTVar cnt succ
-- pure False
unless dry do
rm migrateDir
-- unless ( placed == Just hs ) do
-- err $ red "NCQ write error" <+> pretty fn
-- next True
exitSuccess
-- when flush do
-- liftIO (ncqStorageFlush ncq)
else
exitFailure
-- for_ placed $ \hx -> atomically do
-- writeTQueue checkQ (Just hx)
-- modifyTVar checkN succ
-- info $ green "ok" <+> "B" <+> fill 44 (pretty placed) <+> pretty sz
-- pure True
-- unless norefs do
-- glob ["**/*"] [] r $ \fn -> flip runContT pure $ callCC \next -> do
-- let ref = nameToHash fn
-- ncqRef <- liftIO $ ncqStorageGetRef ncq ref
-- when (isJust ncqRef) do
-- info $ yellow "keep" <+> "R" <+> pretty ref
-- next True
-- refTo <- liftIO (readFile fn)
-- <&> coerce @_ @HashRef . fromString @(Hash HbSync)
-- here <- liftIO (ncqLocate ncq refTo)
-- if isJust here then do
-- liftIO $ ncqStorageSetRef ncq ref refTo
-- info $ green "ok" <+> "R" <+> pretty ref <+> pretty refTo
-- else do
-- warn $ red "Missed block for ref" <+> pretty ref <+> pretty refTo
-- pure True
-- liftIO $ ncqIndexRightNow ncq
-- info $ "check migration / wait to complete"
-- atomically $ writeTQueue checkQ Nothing
-- wait rmp
-- num <- readTVarIO checkN
-- when (num == 0) $ exit ()
-- ee <- readTVarIO errors
-- rest <- readTVarIO checkN
-- liftIO $ hPutDoc stdout $ "errors" <+> pretty ee <+> "leftovers" <+> pretty rest
-- liftIO do
-- if ee == 0 && rest == 0 then do
-- unless dry do
-- rm migrateDir
-- exitSuccess
-- else
-- exitFailure

View File

@ -39,6 +39,49 @@ data PeerTcpProbeWaitKey
data PeerUseHttpDownload
data PeerBrainsDBPath
data PeerListenKey
data PeerKeyFileKey
data PeerStorageKey
data PeerDebugKey
data PeerTraceKey
data PeerTrace1Key
data PeerProxyFetchKey
data PeerTcpSOCKS5
data PeerDownloadThreadKey
instance HasCfgKey PeerDebugKey a where
key = "debug"
instance HasCfgKey PeerTraceKey a where
key = "trace"
instance HasCfgKey PeerTrace1Key a where
key = "trace1"
instance HasCfgKey PeerListenKey (Maybe String) where
key = "listen"
instance HasCfgKey PeerKeyFileKey (Maybe String) where
key = "key"
instance HasCfgKey PeerStorageKey (Maybe String) where
key = "storage"
instance HasCfgKey PeerProxyFetchKey (Set String) where
key = "proxy-fetch-for"
-- NOTE: socks5-auth
-- Network.Simple.TCP does not support
-- SOCKS5 authentification
instance HasCfgKey PeerTcpSOCKS5 (Maybe String) where
key = "tcp.socks5"
instance HasCfgKey PeerDownloadThreadKey (Maybe Int) where
key = "download-threads"
newtype PeerHttpPort = PeerHttpPort (Maybe Integer)
deriving newtype (Pretty)

View File

@ -31,7 +31,7 @@ import HBS2.Net.Proto.Notify
import HBS2.Peer.Proto.Mailbox
import HBS2.OrDie
import HBS2.Storage.Simple
import HBS2.Storage.NCQ
import HBS2.Storage.NCQ3
import HBS2.Storage.Operations.Missed
import HBS2.Data.Detect
@ -155,46 +155,6 @@ defStorageThreads :: Integral a => a
defStorageThreads = 8
data PeerListenKey
data PeerKeyFileKey
data PeerStorageKey
data PeerDebugKey
data PeerTraceKey
data PeerTrace1Key
data PeerProxyFetchKey
data PeerTcpSOCKS5
data PeerDownloadThreadKey
instance HasCfgKey PeerDebugKey a where
key = "debug"
instance HasCfgKey PeerTraceKey a where
key = "trace"
instance HasCfgKey PeerTrace1Key a where
key = "trace1"
instance HasCfgKey PeerListenKey (Maybe String) where
key = "listen"
instance HasCfgKey PeerKeyFileKey (Maybe String) where
key = "key"
instance HasCfgKey PeerStorageKey (Maybe String) where
key = "storage"
instance HasCfgKey PeerProxyFetchKey (Set String) where
key = "proxy-fetch-for"
-- NOTE: socks5-auth
-- Network.Simple.TCP does not support
-- SOCKS5 authentification
instance HasCfgKey PeerTcpSOCKS5 (Maybe String) where
key = "tcp.socks5"
instance HasCfgKey PeerDownloadThreadKey (Maybe Int) where
key = "download-threads"
data PeerOpts =
PeerOpts
@ -825,7 +785,7 @@ runPeer opts = respawnOnError opts $ flip runContT pure do
let pref = fromMaybe xdg (view storage opts <|> storConf)
let ncqPath = coerce pref </> "ncq"
let ncqPath = coerce pref </> "ncq3"
debug $ "storage prefix:" <+> pretty ncqPath
@ -864,7 +824,7 @@ runPeer opts = respawnOnError opts $ flip runContT pure do
-- error "STOP"
s <- lift $ ncqStorageOpen ncqPath
s <- ContT $ ncqWithStorage ncqPath
-- simpleStorageInit @HbSync (Just pref)
let blk = liftIO . hasBlock s
@ -1435,6 +1395,7 @@ checkMigration prefix = flip runContT pure $ callCC \exit -> do
already <- Sy.doesDirectoryExist migration
when (L.null blocks && not already) do
checkNCQ1
exit ()
let reason = if already then
@ -1451,3 +1412,15 @@ checkMigration prefix = flip runContT pure $ callCC \exit -> do
where
checkNCQ1 = do
let ncq1Dir = prefix </> "ncq"
ncq1Here <- Sy.doesDirectoryExist ncq1Dir
when ncq1Here do
notice $ yellow "found NCQv1 storage"
notice $ red "Run" <+> "hbs2-peer migrate" <+> pretty prefix
<> line
<> "to migrate the storage to a new version"
notice $ "You may also: backup" <+> pretty ncq1Dir <+> "or move it or remove permanently"
liftIO exitFailure

View File

@ -189,6 +189,7 @@ library
HBS2.Peer.RPC.Internal.Storage
HBS2.Peer.RPC.Internal.Types
HBS2.Peer.CLI.Detect
HBS2.Peer.NCQ3.Migrate.NCQ
other-modules:
-- HBS2.System.Logger.Simple

View File

@ -0,0 +1,146 @@
module HBS2.Peer.NCQ3.Migrate.NCQ where
import HBS2.Base58
import HBS2.Data.Types.Refs
import HBS2.Hash
import HBS2.Net.Auth.Schema()
import HBS2.Peer.CLI.Detect
import HBS2.Peer.RPC.API.LWWRef
import HBS2.Peer.RPC.API.Peer
import HBS2.Peer.RPC.API.RefLog
import HBS2.Peer.RPC.API.Storage
import HBS2.Peer.RPC.Client
import HBS2.Peer.RPC.Client.Unix
import HBS2.Storage
import HBS2.Storage.NCQ3
import HBS2.Storage.NCQ3.Internal.Files
import HBS2.Storage.NCQ3.Internal.Fossil
import HBS2.Storage.NCQ3.Internal.Index
import HBS2.Storage.NCQ3.Internal.Prelude
import HBS2.Peer.Proto.RefLog
import HBS2.Peer.Proto.RefChan
import HBS2.Peer.Proto.LWWRef
import Data.Config.Suckless.System
import Data.Config.Suckless.Script
import Data.ByteString.Lazy qualified as LBS
import Data.ByteString qualified as BS
import Control.Monad.Trans.Cont
import Control.Monad.Trans.Maybe
import Data.HashSet (HashSet)
import Data.HashSet qualified as HS
import Data.HashMap.Strict qualified as HM
import Data.HashMap.Strict (HashMap)
import Data.Either
import Data.List qualified as L
import System.Directory (getModificationTime)
import UnliftIO.IO
import Streaming.Prelude qualified as S
{- HLINT ignore "Functor law"-}
data WrapRef = forall a . ( Hashed HbSync a
, Pretty a
, RefMetaData a
)
=> WrapRef a
instance RefMetaData WrapRef where
refMetaData (WrapRef a) = refMetaData a
instance Pretty WrapRef where
pretty (WrapRef wtf) = pretty wtf
instance Hashed HbSync WrapRef where
hashObject (WrapRef a) = hashObject a
legacyHashRefNCQ1 :: HashRef -> HashRef -> HashRef
legacyHashRefNCQ1 salt h = HashRef (hashObject (coerce @_ @ByteString h <> coerce salt))
legacyHashRefFor :: Hashed HbSync p => HashRef -> p -> HashRef
legacyHashRefFor salt x = legacyHashRefNCQ1 salt (HashRef $ hashObject @HbSync $ x)
migrateNCQ1 :: MonadUnliftIO m
=> ( Doc AnsiStyle -> IO () )
-> [WrapRef]
-> FilePath
-> FilePath
-> m ()
migrateNCQ1 logger refs src b = flip runContT pure do
debug "migrate NCQ to NCQ3"
let a0 = src </> "0"
a0here <- doesPathExist a0
let a = if a0here then a0 else src
ncq <- ContT $ ncqWithStorage b
let sto = AnyStorage ncq
let mlog = ncqGetFileName ncq "migrate-ncq1.log"
info $ "set SOURCE dir" <+> pretty a
ncqSalt <- liftIO (try @_ @IOException (BS.readFile (src </> ".seed")))
<&> fromRight mempty
<&> HashRef . hashObject
notice $ "ref. salt" <+> pretty ncqSalt
files <- do
fs <- dirFiles a <&> L.filter (\x -> takeExtension x == ".data")
mtimes <- liftIO $ mapM getModificationTime fs
pure [ f | (f,_) <- L.sortOn snd (zip fs mtimes) ]
touch mlog
processed <- HS.fromList . lines <$> liftIO (readFile mlog)
let refz = HM.fromList [ (legacyHashRefFor ncqSalt r, WrapRef r) | WrapRef r <- refs ]
rv <- newTVarIO ( mempty :: HashMap HashRef (WrapRef, HashRef) )
nblk <- newTVarIO 0
for_ files $ \f -> void $ runMaybeT do
guard ( not $ HS.member f processed )
debug $ "processing" <+> pretty f
ncqStorageScanDataFile0 f $ \offset w key s -> void $ runMaybeT do
(t, bs) <- toMPlus (ncqEntryUnwrapValue s)
case t of
B -> do
let h0 = hashObject @HbSync bs
if h0 /= coerce key then do
err $ red "damaged block" <+> pretty key <+> "skip"
else do
void $ putBlock sto (LBS.fromStrict bs)
atomically $ modifyTVar nblk succ
R -> do
case HM.lookup key refz of
Nothing -> none
Just r -> do
atomically $ modifyTVar rv (HM.insert (HashRef $ hashObject r) (r, HashRef (coerce bs)))
_ -> none
liftIO $ appendFile mlog (f <> "\n")
nb <- readTVarIO nblk
liftIO $ logger $ "moved" <+> pretty nb <+> "blocks"
foundRefs <- readTVarIO rv <&> HM.toList
for_ foundRefs $ \(_, (r, v)) -> do
blk <- hasBlock sto (coerce v)
when (isJust blk) do
liftIO $ logger $ green "update ref" <+> pretty r <+> pretty v
updateRef sto r (coerce v)

View File

@ -18,7 +18,7 @@ module HBS2.Storage.NCQ3
import HBS2.Storage.NCQ3.Internal.Types as Exported
import HBS2.Storage.NCQ3.Internal.Class as Exported
import HBS2.Storage.NCQ3.Internal.Prelude as Exported
-- import HBS2.Storage.NCQ3.Internal.Prelude as Exported
import HBS2.Storage.NCQ3.Internal
import HBS2.Storage.NCQ3.Internal.Run
import HBS2.Storage.NCQ3.Internal.State

View File

@ -17,6 +17,7 @@ import HBS2.Storage
import HBS2.Storage.Simple
import HBS2.Storage.Operations.ByteString
import HBS2.Storage.NCQ3
import HBS2.Storage.NCQ3.Internal.Prelude
import HBS2.Storage.NCQ3.Internal.Files
import HBS2.Storage.NCQ3.Internal.Index
import HBS2.Storage.NCQ3.Internal.Fossil

View File

@ -18,6 +18,7 @@ import HBS2.Storage
import HBS2.Storage.Simple
import HBS2.Storage.Operations.ByteString
import HBS2.Storage.NCQ3
import HBS2.Storage.NCQ3.Internal.Prelude
import HBS2.Storage.NCQ3.Internal.Files
import HBS2.Storage.NCQ3.Internal.Index
import HBS2.Storage.NCQ3.Internal.Fossil

View File

@ -18,6 +18,7 @@ import HBS2.Storage
import HBS2.Storage.Simple
import HBS2.Storage.Operations.ByteString
import HBS2.Storage.NCQ3
import HBS2.Storage.NCQ3.Internal.Prelude
import HBS2.Storage.NCQ3.Internal.Files
import HBS2.Storage.NCQ3.Internal.Index
import HBS2.Storage.NCQ3.Internal.Fossil