{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Ouroboros.Consensus.Node.Recovery (
LastShutDownWasClean (..)
, createCleanShutdownMarker
, hasCleanShutdownMarker
, removeCleanShutdownMarker
, runWithCheckedDB
) where
import Control.Monad (unless, when)
import Control.Tracer (Tracer, traceWith)
import Data.Proxy (Proxy)
import Data.Typeable (Typeable)
import Ouroboros.Consensus.Block (StandardHash)
import Ouroboros.Consensus.Node.Exit (ExitReason (..), toExitReason)
import Ouroboros.Consensus.Storage.ChainDB
import Ouroboros.Consensus.Util.IOLike
import System.FS.API (HasFS, doesFileExist, removeFile, withFile)
import System.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
$c== :: LastShutDownWasClean -> LastShutDownWasClean -> Bool
== :: LastShutDownWasClean -> LastShutDownWasClean -> Bool
$c/= :: LastShutDownWasClean -> LastShutDownWasClean -> Bool
/= :: 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
$cshowsPrec :: Int -> LastShutDownWasClean -> ShowS
showsPrec :: Int -> LastShutDownWasClean -> ShowS
$cshow :: LastShutDownWasClean -> String
show :: LastShutDownWasClean -> String
$cshowList :: [LastShutDownWasClean] -> ShowS
showList :: [LastShutDownWasClean] -> ShowS
Show)
hasCleanShutdownMarker ::
HasFS m h
-> m Bool
hasCleanShutdownMarker :: forall (m :: * -> *) h. HasFS m h -> m Bool
hasCleanShutdownMarker HasFS m h
hasFS =
HasFS m h -> HasCallStack => 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 :: forall (m :: * -> *) h. IOLike m => HasFS m h -> m ()
createCleanShutdownMarker HasFS m h
hasFS = do
alreadyExists <- HasFS m h -> m Bool
forall (m :: * -> *) h. HasFS m h -> m Bool
hasCleanShutdownMarker HasFS m h
hasFS
unless alreadyExists $
withFile hasFS cleanShutdownMarkerFile (WriteMode MustBeNew) $ \Handle h
_h ->
() -> m ()
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
removeCleanShutdownMarker ::
HasFS m h
-> m ()
removeCleanShutdownMarker :: forall (m :: * -> *) h. HasFS m h -> m ()
removeCleanShutdownMarker HasFS m h
hasFS =
HasFS m h -> HasCallStack => 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 :: forall blk.
(StandardHash blk, Typeable blk) =>
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
-> Tracer m (TraceEvent blk)
-> HasFS m h
-> (LastShutDownWasClean -> (ChainDB m blk -> m a -> m a) -> m a)
-> m a
runWithCheckedDB :: forall a (m :: * -> *) h blk.
(IOLike m, StandardHash blk, Typeable blk) =>
Proxy blk
-> Tracer m (TraceEvent blk)
-> HasFS m h
-> (LastShutDownWasClean -> (ChainDB m blk -> m a -> m a) -> m a)
-> m a
runWithCheckedDB Proxy blk
pb Tracer m (TraceEvent blk)
tracer HasFS m h
hasFS LastShutDownWasClean -> (ChainDB m blk -> m a -> m a) -> m a
body = do
wasClean <- HasFS m h -> m Bool
forall (m :: * -> *) h. HasFS m h -> m Bool
hasCleanShutdownMarker HasFS m h
hasFS
unless wasClean (traceWith tracer TraceLastShutdownUnclean)
removeMarkerOnUncleanShutdown wasClean
$ body
(LastShutDownWasClean 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 :: forall (m :: * -> *) e a.
(IOLike m, Exception e) =>
(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 e a. Exception e => 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 e a. Exception e => e -> m a
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwIO e
e