Skip to content

Commit 30a9ea1

Browse files
committed
create Show instance for FetchError, move shouldAbortOnPanic
1 parent 9abb759 commit 30a9ea1

File tree

11 files changed

+86
-87
lines changed

11 files changed

+86
-87
lines changed

cardano-db-sync/app/http-get-json-metadata.hs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@ import Cardano.DbSync (
66
SimplifiedPoolOfflineData (..),
77
httpGetPoolOfflineData,
88
parsePoolUrl,
9-
renderFetchError,
109
)
1110
import Control.Monad.IO.Class (liftIO)
12-
import Control.Monad.Trans.Except (ExceptT)
13-
import Control.Monad.Trans.Except.Exit (orDie)
11+
import Control.Monad.Trans.Except (ExceptT, runExceptT)
1412
import qualified Data.ByteString.Base16 as Base16
1513
import qualified Data.ByteString.Char8 as BS
1614
import qualified Data.Text as Text
@@ -26,6 +24,7 @@ import System.Console.ANSI.Types (
2624
)
2725
import System.Environment (getArgs, getProgName)
2826
import System.Exit (exitFailure)
27+
import Cardano.DbSync.Error (runOrThrowIO)
2928

3029
main :: IO ()
3130
main = do
@@ -60,7 +59,7 @@ usageExit = do
6059

6160
runHttpGet :: PoolUrl -> Maybe PoolMetaHash -> IO ()
6261
runHttpGet poolUrl mHash =
63-
reportSuccess =<< orDie renderFetchError httpGet
62+
reportSuccess =<< runOrThrowIO (runExceptT httpGet)
6463
where
6564
httpGet :: ExceptT FetchError IO SimplifiedPoolOfflineData
6665
httpGet = do

cardano-db-sync/app/test-http-get-json-metadata.hs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import Cardano.DbSync.Era.Shelley.Offline.Http (
1717
FetchError (..),
1818
httpGetPoolOfflineData,
1919
parsePoolUrl,
20-
renderFetchError,
2120
)
2221
import Control.Monad (foldM)
2322
import Control.Monad.IO.Class (MonadIO)
@@ -28,7 +27,6 @@ import qualified Data.List as List
2827
import qualified Data.List.Extra as List
2928
import Data.Ord (Down (..))
3029
import Data.Text (Text)
31-
import qualified Data.Text.IO as Text
3230
import Database.Esqueleto.Experimental (
3331
SqlBackend,
3432
from,
@@ -65,7 +63,7 @@ main = do
6563
httpGetPoolOfflineData manager request poolUrl mHash
6664
case eres of
6765
Left err -> do
68-
Text.putStrLn $ renderFetchError err
66+
putStrLn $ show err
6967
pure $ classifyFetchError accum err
7068
Right _ ->
7169
pure accum

cardano-db-sync/cardano-db-sync.cabal

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,6 @@ executable http-get-json-metadata
265265
, http-client-tls
266266
, text
267267
, transformers
268-
, transformers-except
269268

270269
executable test-http-get-json-metadata
271270
default-language: Haskell2010

cardano-db-sync/src/Cardano/DbSync.hs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ module Cardano.DbSync (
2323
SimplifiedPoolOfflineData (..),
2424
httpGetPoolOfflineData,
2525
parsePoolUrl,
26-
renderFetchError,
2726
) where
2827

2928
import Cardano.BM.Trace (Trace, logError, logInfo, logWarning)
@@ -52,16 +51,14 @@ import Cardano.DbSync.Era.Shelley.Offline.Http (
5251
SimplifiedPoolOfflineData (..),
5352
httpGetPoolOfflineData,
5453
parsePoolUrl,
55-
renderFetchError,
5654
spodJson,
5755
)
58-
import Cardano.DbSync.Error (SyncNodeError, runOrThrowIO)
56+
import Cardano.DbSync.Error (SyncNodeError, runOrThrowIO, hasAbortOnPanicEnv)
5957
import Cardano.DbSync.Ledger.State
6058
import Cardano.DbSync.Rollback (unsafeRollback)
6159
import Cardano.DbSync.Sync (runSyncNodeClient)
6260
import Cardano.DbSync.Tracing.ToObjectOrphans ()
6361
import Cardano.DbSync.Types
64-
import Cardano.DbSync.Util (readAbortOnPanic)
6562
import Cardano.Prelude hiding (Nat, (%))
6663
import Cardano.Slotting.Slot (EpochNo (..))
6764
import Control.Concurrent.Async
@@ -80,7 +77,7 @@ runDbSyncNode metricsSetters knownMigrations params =
8077
withIOManager $ \iomgr -> do
8178
trce <- configureLogging params "db-sync-node"
8279

83-
aop <- readAbortOnPanic
80+
aop <- hasAbortOnPanicEnv
8481
startupReport trce aop params
8582

8683
runDbSync metricsSetters knownMigrations iomgr trce params aop

cardano-db-sync/src/Cardano/DbSync/Database.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ rollbackLedger syncEnv point =
130130
let statePoint = headerStatePoint $ headerState $ clsState st
131131
-- This is an extra validation that should always succeed.
132132
unless (point == statePoint) $
133-
logAndPanic (getTrace syncEnv) $
133+
logAndThrowIO (getTrace syncEnv) $
134134
mconcat
135135
[ "Ledger "
136136
, textShow statePoint
@@ -152,7 +152,7 @@ validateConsistentLevel syncEnv stPoint = do
152152
compareTips stPoint dbTipInfo cLevel
153153
where
154154
compareTips _ dbTip Unchecked =
155-
logAndPanic tracer $
155+
logAndThrowIO tracer $
156156
"Found Unchecked Consistent Level. " <> showContext dbTip Unchecked
157157
compareTips (Point Origin) Nothing Consistent = pure ()
158158
compareTips (Point Origin) _ DBAheadOfLedger = pure ()
@@ -163,7 +163,7 @@ validateConsistentLevel syncEnv stPoint = do
163163
compareTips (Point (At blk)) (Just tip) DBAheadOfLedger
164164
| blockPointSlot blk <= bSlotNo tip = pure ()
165165
compareTips _ dbTip cLevel =
166-
logAndPanic tracer $
166+
logAndThrowIO tracer $
167167
"Unexpected Consistent Level. " <> showContext dbTip cLevel
168168

169169
tracer = getTrace syncEnv

cardano-db-sync/src/Cardano/DbSync/Era/Shelley/Offline.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,6 @@ fetchOfflineData _tracer manager time pfr =
136136
{ DB.poolOfflineFetchErrorPoolId = pfrPoolHashId pfr
137137
, DB.poolOfflineFetchErrorFetchTime = Time.posixSecondsToUTCTime time
138138
, DB.poolOfflineFetchErrorPmrId = pfrReferenceId pfr
139-
, DB.poolOfflineFetchErrorFetchError = renderFetchError err
139+
, DB.poolOfflineFetchErrorFetchError = show err
140140
, DB.poolOfflineFetchErrorRetryCount = retryCount (pfrRetry pfr)
141141
}

cardano-db-sync/src/Cardano/DbSync/Era/Shelley/Offline/Http.hs

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{-# LANGUAGE DeriveGeneric #-}
22
{-# LANGUAGE OverloadedStrings #-}
33
{-# LANGUAGE NoImplicitPrelude #-}
4+
{-# LANGUAGE LambdaCase #-}
45

56
module Cardano.DbSync.Era.Shelley.Offline.Http (
67
FetchError (..),
78
SimplifiedPoolOfflineData (..),
89
httpGetPoolOfflineData,
910
parsePoolUrl,
10-
renderFetchError,
1111
) where
1212

1313
import qualified Cardano.Crypto.Hash.Blake2b as Crypto
@@ -31,6 +31,7 @@ import qualified Data.Text.Encoding as Text
3131
import Network.HTTP.Client (HttpException (..))
3232
import qualified Network.HTTP.Client as Http
3333
import qualified Network.HTTP.Types as Http
34+
import qualified GHC.Show as GHCS
3435

3536
-- | Fetch error for the HTTP client fetching the pool offline metadata.
3637
data FetchError
@@ -47,6 +48,45 @@ data FetchError
4748
| FEConnectionFailure !PoolUrl
4849
deriving (Eq, Generic)
4950

51+
instance Exception FetchError
52+
53+
instance Show FetchError where
54+
show =
55+
\case
56+
FEHashMismatch (PoolUrl url) xpt act ->
57+
mconcat
58+
[ "Hash mismatch from when fetching metadata from "
59+
, show url
60+
, ". Expected "
61+
, show xpt
62+
, " but got "
63+
, show act
64+
, "."
65+
]
66+
FEDataTooLong (PoolUrl url) ->
67+
mconcat
68+
["Offline pool data when fetching metadata from ", show url, " exceeded 512 bytes."]
69+
FEUrlParseFail (PoolUrl url) err ->
70+
mconcat
71+
["URL parse error for ", show url, " resulted in : ", show err]
72+
FEJsonDecodeFail (PoolUrl url) err ->
73+
mconcat
74+
["JSON decode error from when fetching metadata from ", show url, " resulted in : ", show err]
75+
FEHttpException (PoolUrl url) err ->
76+
mconcat ["HTTP Exception for ", show url, " resulted in : ", show err]
77+
FEHttpResponse (PoolUrl url) sc msg ->
78+
mconcat ["HTTP Response from ", show url, " resulted in HTTP status code : ", show sc, " ", show msg]
79+
FEBadContentType (PoolUrl url) ct ->
80+
mconcat ["HTTP Response from ", show url, ": expected JSON, but got : ", show ct]
81+
FEBadContentTypeHtml (PoolUrl url) ct ->
82+
mconcat ["HTTP Response from ", show url, ": expected JSON, but got : ", show ct]
83+
FETimeout (PoolUrl url) ctx ->
84+
mconcat ["Timeout when fetching metadata from ", show url, ": ", show ctx]
85+
FEConnectionFailure (PoolUrl url) ->
86+
mconcat
87+
["Connection failure when fetching metadata from ", show url, "'."]
88+
FEIOException err -> "IO Exception: " <> show err
89+
5090
data SimplifiedPoolOfflineData = SimplifiedPoolOfflineData
5191
{ spodTickerName :: !Text
5292
, spodHash :: !ByteString
@@ -158,43 +198,6 @@ parsePoolUrl poolUrl =
158198
wrapHttpException :: HttpException -> FetchError
159199
wrapHttpException err = FEHttpException poolUrl (textShow err)
160200

161-
renderFetchError :: FetchError -> Text
162-
renderFetchError fe =
163-
case fe of
164-
FEHashMismatch (PoolUrl url) xpt act ->
165-
mconcat
166-
[ "Hash mismatch from when fetching metadata from "
167-
, url
168-
, ". Expected "
169-
, xpt
170-
, " but got "
171-
, act
172-
, "."
173-
]
174-
FEDataTooLong (PoolUrl url) ->
175-
mconcat
176-
["Offline pool data when fetching metadata from ", url, " exceeded 512 bytes."]
177-
FEUrlParseFail (PoolUrl url) err ->
178-
mconcat
179-
["URL parse error for ", url, " resulted in : ", err]
180-
FEJsonDecodeFail (PoolUrl url) err ->
181-
mconcat
182-
["JSON decode error from when fetching metadata from ", url, " resulted in : ", err]
183-
FEHttpException (PoolUrl url) err ->
184-
mconcat ["HTTP Exception for ", url, " resulted in : ", err]
185-
FEHttpResponse (PoolUrl url) sc msg ->
186-
mconcat ["HTTP Response from ", url, " resulted in HTTP status code : ", textShow sc, " ", msg]
187-
FEBadContentType (PoolUrl url) ct ->
188-
mconcat ["HTTP Response from ", url, ": expected JSON, but got : ", ct]
189-
FEBadContentTypeHtml (PoolUrl url) ct ->
190-
mconcat ["HTTP Response from ", url, ": expected JSON, but got : ", ct]
191-
FETimeout (PoolUrl url) ctx ->
192-
mconcat ["Timeout when fetching metadata from ", url, ": ", ctx]
193-
FEConnectionFailure (PoolUrl url) ->
194-
mconcat
195-
["Connection failure when fetching metadata from ", url, "'."]
196-
FEIOException err -> "IO Exception: " <> err
197-
198201
-- -------------------------------------------------------------------------------------------------
199202

200203
convertHttpException :: PoolUrl -> HttpException -> FetchError

cardano-db-sync/src/Cardano/DbSync/Era/Shelley/ValidateWithdrawal.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module Cardano.DbSync.Era.Shelley.ValidateWithdrawal (
99
import Cardano.BM.Trace (Trace, logError)
1010
import Cardano.Db (Ada (..))
1111
import qualified Cardano.Db as Db
12+
import Cardano.DbSync.Error (shouldAbortOnPanic)
1213
import Cardano.DbSync.Util
1314
import Cardano.Slotting.Slot (EpochNo (..))
1415
import Control.Monad.IO.Class (MonadIO, liftIO)
@@ -60,7 +61,7 @@ validateRewardWithdrawals trce (EpochNo epochNo) = do
6061
logError trce . mconcat $
6162
[textShow epochNo, ": ", textShow (length xs), " errors, eg\n"]
6263
++ List.intersperse "\n" (map reportError xs)
63-
panicAbort "Validation failure"
64+
shouldAbortOnPanic "Validation failure"
6465

6566
-- -----------------------------------------------------------------------------
6667

cardano-db-sync/src/Cardano/DbSync/Error.hs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
{-# LANGUAGE LambdaCase #-}
12
{-# LANGUAGE OverloadedStrings #-}
2-
{-# LANGUAGE NoImplicitPrelude #-}
33
{-# LANGUAGE RankNTypes #-}
4-
{-# LANGUAGE LambdaCase #-}
4+
{-# LANGUAGE NoImplicitPrelude #-}
55

66
module Cardano.DbSync.Error (
77
SyncInvariant (..),
@@ -11,7 +11,10 @@ module Cardano.DbSync.Error (
1111
dbSyncNodeError,
1212
dbSyncInvariant,
1313
renderSyncInvariant,
14-
runOrThrowIO
14+
runOrThrowIO,
15+
logAndThrowIO,
16+
shouldAbortOnPanic,
17+
hasAbortOnPanicEnv,
1518
) where
1619

1720
import qualified Cardano.Chain.Genesis as Byron
@@ -26,6 +29,10 @@ import qualified Data.Text as Text
2629
import qualified Data.Text.Encoding as Text
2730
import qualified GHC.Show as GShow
2831
import qualified Text.Show as Text
32+
import System.Environment (lookupEnv)
33+
import System.Posix.Process (exitImmediately)
34+
import Cardano.BM.Trace (Trace, logError)
35+
import GHC.IO.Exception (userError)
2936

3037
data SyncInvariant
3138
= EInvInOut !Word64 !Word64
@@ -131,3 +138,23 @@ runOrThrowIO ioEither = do
131138
case et of
132139
Left err -> throwIO err
133140
Right a -> pure a
141+
142+
logAndThrowIO :: Trace IO Text -> Text -> IO ()
143+
logAndThrowIO tracer msg = do
144+
logError tracer msg
145+
throwIO $ userError $ show msg
146+
147+
-- The network code catches all execptions and retries them, even exceptions generated by the
148+
-- 'error' or 'panic' function. To actually force the termination of 'db-sync' we therefore
149+
-- need a custom panic function that is guaranteed to abort when we want it to.
150+
-- However, we may not want to abort in production, so we make it optional by use of an
151+
-- environment variable.
152+
shouldAbortOnPanic :: Text -> IO ()
153+
shouldAbortOnPanic msg = do
154+
whenM hasAbortOnPanicEnv $ do
155+
threadDelay 100000 -- 0.1 seconds
156+
mapM_ putStrLn ["DbSyncAbortOnPanic: ", msg]
157+
exitImmediately (ExitFailure 1)
158+
159+
hasAbortOnPanicEnv :: IO Bool
160+
hasAbortOnPanicEnv = isJust <$> lookupEnv "DbSyncAbortOnPanic"

cardano-db-sync/src/Cardano/DbSync/Util.hs

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,7 @@ module Cardano.DbSync.Util (
1515
maybeFromStrict,
1616
maybeToStrict,
1717
nullMetricSetters,
18-
panicAbort,
19-
logAndPanic,
2018
plusCoin,
21-
readAbortOnPanic,
2219
renderByteArray,
2320
renderPoint,
2421
renderSlotList,
@@ -54,16 +51,13 @@ import qualified Data.Map.Strict as Map
5451
import qualified Data.Strict.Maybe as Strict
5552
import qualified Data.Text as Text
5653
import qualified Data.Text.Encoding as Text
57-
import qualified Data.Text.IO as Text
5854
import qualified Data.Time.Clock as Time
5955
import Ouroboros.Consensus.Block.Abstract (ConvertRawHash (..))
6056
import Ouroboros.Consensus.Protocol.Praos.Translate ()
6157
import Ouroboros.Consensus.Shelley.HFEras ()
6258
import Ouroboros.Consensus.Shelley.Ledger.SupportsProtocol ()
6359
import Ouroboros.Network.Block (blockSlot, getPoint)
6460
import qualified Ouroboros.Network.Point as Point
65-
import System.Environment (lookupEnv)
66-
import System.Posix.Process (exitImmediately)
6761
import Text.Show.Pretty (ppShow)
6862

6963
cardanoBlockSlotNo :: CardanoBlock -> SlotNo
@@ -140,29 +134,9 @@ nullMetricSetters =
140134
, metricsSetDbSlotHeight = const $ pure ()
141135
}
142136

143-
-- The network code catches all execptions and retries them, even exceptions generated by the
144-
-- 'error' or 'panic' function. To actually force the termination of 'db-sync' we therefore
145-
-- need a custom panic function that is guaranteed to abort when we want it to.
146-
-- However, we may not want to abort in production, so we make it optional by use of an
147-
-- environment variable.
148-
panicAbort :: Text -> IO ()
149-
panicAbort msg = do
150-
whenM readAbortOnPanic $ do
151-
threadDelay 100000 -- 0.1 seconds
152-
mapM_ Text.putStrLn ["panic abort:", msg]
153-
exitImmediately (ExitFailure 1)
154-
155-
logAndPanic :: Trace IO Text -> Text -> IO ()
156-
logAndPanic tracer msg = do
157-
logError tracer msg
158-
panic msg
159-
160137
plusCoin :: Coin -> Coin -> Coin
161138
plusCoin (Coin a) (Coin b) = Coin (a + b)
162139

163-
readAbortOnPanic :: IO Bool
164-
readAbortOnPanic = isJust <$> lookupEnv "DbSyncAbortOnPanic"
165-
166140
renderByteArray :: ByteArrayAccess bin => bin -> Text
167141
renderByteArray =
168142
Text.decodeUtf8 . Base16.encode . Data.ByteArray.convert

0 commit comments

Comments
 (0)