{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Ouroboros.Consensus.Node.Recovery (
LastShutDownWasClean (..)
, createCleanShutdownMarker
, hasCleanShutdownMarker
, removeCleanShutdownMarker
, runWithCheckedDB
) where
import Control.Monad (unless, when)
import Data.Proxy (Proxy)
import Data.Typeable (Typeable)
import Ouroboros.Consensus.Block (StandardHash)
import Ouroboros.Consensus.Node.Exit (ExitReason (..), toExitReason)
import Ouroboros.Consensus.Util.IOLike
import Ouroboros.Consensus.Storage.ChainDB
import Ouroboros.Consensus.Storage.FS.API (HasFS, doesFileExist,
removeFile, withFile)
import Ouroboros.Consensus.Storage.FS.API.Types (AllowExisting (..),
FsPath, OpenMode (..), mkFsPath)
cleanShutdownMarkerFile :: FsPath
cleanShutdownMarkerFile :: FsPath
cleanShutdownMarkerFile = [String] -> FsPath
mkFsPath [String
"clean"]
newtype LastShutDownWasClean = LastShutDownWasClean Bool
deriving (LastShutDownWasClean -> LastShutDownWasClean -> Bool
(LastShutDownWasClean -> LastShutDownWasClean -> Bool)
-> (LastShutDownWasClean -> LastShutDownWasClean -> Bool)
-> Eq LastShutDownWasClean
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: LastShutDownWasClean -> LastShutDownWasClean -> Bool
$c/= :: LastShutDownWasClean -> LastShutDownWasClean -> Bool
== :: LastShutDownWasClean -> LastShutDownWasClean -> Bool
$c== :: LastShutDownWasClean -> LastShutDownWasClean -> Bool
Eq, Int -> LastShutDownWasClean -> ShowS
[LastShutDownWasClean] -> ShowS
LastShutDownWasClean -> String
(Int -> LastShutDownWasClean -> ShowS)
-> (LastShutDownWasClean -> String)
-> ([LastShutDownWasClean] -> ShowS)
-> Show LastShutDownWasClean
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [LastShutDownWasClean] -> ShowS
$cshowList :: [LastShutDownWasClean] -> ShowS
show :: LastShutDownWasClean -> String
$cshow :: LastShutDownWasClean -> String
showsPrec :: Int -> LastShutDownWasClean -> ShowS
$cshowsPrec :: Int -> LastShutDownWasClean -> ShowS
Show)
hasCleanShutdownMarker
:: HasFS m h
-> m Bool
hasCleanShutdownMarker :: HasFS m h -> m Bool
hasCleanShutdownMarker HasFS m h
hasFS =
HasFS m h -> FsPath -> m Bool
forall (m :: * -> *) h.
HasFS m h -> HasCallStack => FsPath -> m Bool
doesFileExist HasFS m h
hasFS FsPath
cleanShutdownMarkerFile
createCleanShutdownMarker
:: IOLike m
=> HasFS m h
-> m ()
createCleanShutdownMarker :: HasFS m h -> m ()
createCleanShutdownMarker HasFS m h
hasFS = do
Bool
alreadyExists <- HasFS m h -> m Bool
forall (m :: * -> *) h. HasFS m h -> m Bool
hasCleanShutdownMarker HasFS m h
hasFS
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
alreadyExists (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
HasFS m h -> FsPath -> OpenMode -> (Handle h -> m ()) -> m ()
forall (m :: * -> *) h a.
(HasCallStack, MonadThrow m) =>
HasFS m h -> FsPath -> OpenMode -> (Handle h -> m a) -> m a
withFile HasFS m h
hasFS FsPath
cleanShutdownMarkerFile (AllowExisting -> OpenMode
WriteMode AllowExisting
MustBeNew) ((Handle h -> m ()) -> m ()) -> (Handle h -> m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ \Handle h
_h ->
() -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
removeCleanShutdownMarker
:: HasFS m h
-> m ()
removeCleanShutdownMarker :: HasFS m h -> m ()
removeCleanShutdownMarker HasFS m h
hasFS =
HasFS m h -> FsPath -> m ()
forall (m :: * -> *) h. HasFS m h -> HasCallStack => FsPath -> m ()
removeFile HasFS m h
hasFS FsPath
cleanShutdownMarkerFile
exceptionRequiresRecovery ::
forall blk. (StandardHash blk, Typeable blk)
=> Proxy blk
-> SomeException
-> Bool
exceptionRequiresRecovery :: Proxy blk -> SomeException -> Bool
exceptionRequiresRecovery Proxy blk
pb SomeException
e = case Proxy blk -> SomeException -> ExitReason
forall blk.
(Typeable blk, StandardHash blk) =>
Proxy blk -> SomeException -> ExitReason
toExitReason Proxy blk
pb SomeException
e of
ExitReason
DatabaseCorruption -> Bool
True
ExitReason
_ -> Bool
False
runWithCheckedDB
:: forall a m h blk. (IOLike m, StandardHash blk, Typeable blk)
=> Proxy blk
-> HasFS m h
-> (LastShutDownWasClean -> (ChainDB m blk -> m a -> m a) -> m a)
-> m a
runWithCheckedDB :: Proxy blk
-> HasFS m h
-> (LastShutDownWasClean -> (ChainDB m blk -> m a -> m a) -> m a)
-> m a
runWithCheckedDB Proxy blk
pb HasFS m h
hasFS LastShutDownWasClean -> (ChainDB m blk -> m a -> m a) -> m a
body = do
Bool
wasClean <- HasFS m h -> m Bool
forall (m :: * -> *) h. HasFS m h -> m Bool
hasCleanShutdownMarker HasFS m h
hasFS
Bool -> m a -> m a
forall a. Bool -> m a -> m a
removeMarkerOnUncleanShutdown Bool
wasClean
(m a -> m a) -> m a -> m a
forall a b. (a -> b) -> a -> b
$ LastShutDownWasClean -> (ChainDB m blk -> m a -> m a) -> m a
body
(Bool -> LastShutDownWasClean
LastShutDownWasClean Bool
wasClean)
(\ChainDB m blk
_cdb m a
runWithInitializedChainDB -> m a -> m a
forall a. m a -> m a
createMarkerOnCleanShutdown (m a -> m a) -> m a -> m a
forall a b. (a -> b) -> a -> b
$ do
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
wasClean (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ HasFS m h -> m ()
forall (m :: * -> *) h. HasFS m h -> m ()
removeCleanShutdownMarker HasFS m h
hasFS
m a
runWithInitializedChainDB
)
where
removeMarkerOnUncleanShutdown :: Bool -> m a -> m a
removeMarkerOnUncleanShutdown Bool
wasClean = if Bool -> Bool
not Bool
wasClean then m a -> m a
forall a. a -> a
id else (SomeException -> Bool) -> m () -> m a -> m a
forall (m :: * -> *) e a.
(IOLike m, Exception e) =>
(e -> Bool) -> m () -> m a -> m a
onExceptionIf
(Proxy blk -> SomeException -> Bool
forall blk.
(StandardHash blk, Typeable blk) =>
Proxy blk -> SomeException -> Bool
exceptionRequiresRecovery Proxy blk
pb)
(HasFS m h -> m ()
forall (m :: * -> *) h. HasFS m h -> m ()
removeCleanShutdownMarker HasFS m h
hasFS)
createMarkerOnCleanShutdown :: m a -> m a
createMarkerOnCleanShutdown = (SomeException -> Bool) -> m () -> m a -> m a
forall (m :: * -> *) e a.
(IOLike m, Exception e) =>
(e -> Bool) -> m () -> m a -> m a
onExceptionIf
(Bool -> Bool
not (Bool -> Bool) -> (SomeException -> Bool) -> SomeException -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Proxy blk -> SomeException -> Bool
forall blk.
(StandardHash blk, Typeable blk) =>
Proxy blk -> SomeException -> Bool
exceptionRequiresRecovery Proxy blk
pb)
(HasFS m h -> m ()
forall (m :: * -> *) h. IOLike m => HasFS m h -> m ()
createCleanShutdownMarker HasFS m h
hasFS)
onExceptionIf
:: (IOLike m, Exception e)
=> (e -> Bool)
-> m ()
-> m a
-> m a
onExceptionIf :: (e -> Bool) -> m () -> m a -> m a
onExceptionIf e -> Bool
p m ()
h m a
m = m a
m m a -> (e -> m a) -> m a
forall (m :: * -> *) e a.
(MonadCatch m, Exception e) =>
m a -> (e -> m a) -> m a
`catch` \e
e -> do
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (e -> Bool
p e
e) m ()
h
e -> m a
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwIO e
e