Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@ pre: "<b>6. </b>"
>
> A version of `cardano-node@1.33.1` patched with the necessary commits can be found at [CardanoSolutions/cardano-node@1.33.1+local-tx-monitor](https://github.com/CardanoSolutions/cardano-node/releases/tag/1.33.1+local-tx-monitor).
>
> - `connectionStatus` in the health object, taking `"connected"` or `"disconnected"` as values to reflect the current connection status with the node. [#154](https://github.com/CardanoSolutions/ogmios/issues/154)
> - New fields in the health object:
> - `connectionStatus` → `"connected"` or `"disconnected"`, to reflect status with the node. [#154](https://github.com/CardanoSolutions/ogmios/issues/154)
> - `currentEpoch` → which returns the current known epoch of the linked node [#164](https://github.com/CardanoSolutions/ogmios/issues/164)
> - `slotInEpoch` → which returns the relative number of slots elapsed in the current epoch [#164](https://github.com/CardanoSolutions/ogmios/issues/154)
>
> - New `ogmios health-check` command, useful to perform simple health check on a running server. For example, to monitor a container via Docker health check mechanism:
> ```Dockerfile
> HEALTHCHECK --interval=10s --timeout=5s --retries=1 CMD /bin/ogmios health-check
> ```
> - Bumped internal dependencies to Cardano's 1.33.0 eco-system.
> - Bumped internal dependencies to Cardano's 1.33.* eco-system.

#### Changed

Expand Down
6 changes: 5 additions & 1 deletion clients/TypeScript/packages/client/test/ServerHealth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ const expectHealth = (obj: any): void => {
'lastTipUpdate',
'metrics',
'startTime',
'networkSynchronization'
'networkSynchronization',
'currentEpoch',
'slotInEpoch'
]))
expect(obj.currentEpoch).not.toBe(null)
expect(obj.slotInEra).not.toBe(null)
}

describe('ServerHealth', () => {
Expand Down
6 changes: 5 additions & 1 deletion docs/content/getting-started/monitoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ $ curl -H 'Accept: application/json' http://localhost:1337/health
},
"networkSynchronization": 0.99,
"currentEra": "Mary",
"connectionStatus": "disconnected"
"connectionStatus": "disconnected",
"currentEpoch": 164,
"slotInEpoch": 324543
}
```

Expand All @@ -59,6 +61,8 @@ All information are computed at runtime and **not preserved between restarts** (
| `lastKnownTip` | Last known chain tip received from the node (can be `null`) |
| `networkSynchronization` | A **(nullable)** percentage indicator of how far the server/node is from the network tip. `1` means it is synchronized. |
| `currentEra` | The **(nullable)** current Cardano era of the underlying node. Useful for state-queries and debugging. |
| `currentEpoch` | The **(nullable)** current epoch number known of the underlying node. |
| `slotInEpoch` | The **(nullable)** relative slot number within the current epoch. |
| `metrics.activeConnections` | Number of WebSocket connections currently established with the server. |
| `metrics.totalConnections` | Total number of WebSocket connections established with the server since it's started. |
| `metrics.sessionDurations` | Some time measures (`min`, `max`, `mean`) of the duration of each sessions, in milliseconds. |
Expand Down
33 changes: 23 additions & 10 deletions server/src/Ogmios/App/Health.hs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@ import Ogmios.Control.MonadSTM
import Ogmios.Data.Health
( CardanoEra (..)
, ConnectionStatus (..)
, EpochNo (..)
, Health (..)
, NetworkSynchronization
, SlotInEpoch (..)
, emptyHealth
, mkNetworkSynchronization
, modifyHealth
Expand All @@ -79,7 +81,7 @@ import Network.TypedProtocol.Pipelined
import Ouroboros.Consensus.HardFork.Combinator
( HardForkBlock, eraIndexToInt )
import Ouroboros.Consensus.HardFork.History.Qry
( interpretQuery, slotToWallclock )
( interpretQuery, slotToEpoch', slotToWallclock )
import Ouroboros.Network.Block
( Point (..), Tip (..), genesisPoint, getTipPoint )
import Ouroboros.Network.NodeToClient
Expand Down Expand Up @@ -135,13 +137,19 @@ newHealthCheckClient tr Debouncer{debounce} = do
{ chainSyncClient = mkHealthCheckClient $ \lastKnownTip -> debounce $ do
tvar <- asks (view typed)
metrics <- join (Metrics.sample <$> asks (view typed) <*> asks (view typed))
(now, Just -> networkSynchronization, Just -> currentEra) <- getNetworkInformation lastKnownTip
( now
, Just -> networkSynchronization
, (Just -> currentEpoch, Just -> slotInEpoch)
, Just -> currentEra
) <- getNetworkInformation lastKnownTip
health <- modifyHealth tvar $ \h -> h
{ lastKnownTip
, lastTipUpdate = Just now
, networkSynchronization
, currentEra
, metrics
, currentEpoch
, slotInEpoch
, connectionStatus = Connected
}
logWith tr (HealthTick health)
Expand Down Expand Up @@ -268,23 +276,23 @@ newTimeInterpreterClient
, HasType NetworkParameters env
)
=> m ( LocalStateQueryClient block (Point block) (Ledger.Query block) m ()
, Tip block -> m (UTCTime, NetworkSynchronization, CardanoEra)
, Tip block -> m (UTCTime, NetworkSynchronization, (EpochNo, SlotInEpoch), CardanoEra)
)
newTimeInterpreterClient = do
notifyTip <- atomically newEmptyTMVar
getResult <- atomically newEmptyTMVar
return
( LocalStateQueryClient $ clientStIdle
(atomically $ takeTMVar notifyTip)
(\a0 a1 a2 -> atomically $ putTMVar getResult (a0, a1, a2))
(\a0 a1 a2 a3 -> atomically $ putTMVar getResult (a0, a1, a2, a3))
, \tip -> do
atomically $ putTMVar notifyTip tip
atomically $ takeTMVar getResult
)
where
clientStIdle
:: m (Tip block)
-> (UTCTime -> NetworkSynchronization -> CardanoEra -> m ())
-> (UTCTime -> NetworkSynchronization -> (EpochNo, SlotInEpoch) -> CardanoEra -> m ())
-> m (LSQ.ClientStIdle block (Point block) (Ledger.Query block) m ())
clientStIdle getTip notifyResult =
pure $ LSQ.SendMsgAcquire Nothing $ LSQ.ClientStAcquiring
Expand All @@ -296,7 +304,7 @@ newTimeInterpreterClient = do

clientStAcquired
:: m (Tip block)
-> (UTCTime -> NetworkSynchronization -> CardanoEra -> m ())
-> (UTCTime -> NetworkSynchronization -> (EpochNo, SlotInEpoch) -> CardanoEra -> m ())
-> m (LSQ.ClientStAcquired block (Point block) (Ledger.Query block) m ())
clientStAcquired getTip notifyResult = do
tip <- getTip
Expand All @@ -310,7 +318,7 @@ newTimeInterpreterClient = do
}

clientStQuerySlotTime
:: (UTCTime -> NetworkSynchronization -> CardanoEra -> m ())
:: (UTCTime -> NetworkSynchronization -> (EpochNo, SlotInEpoch) -> CardanoEra -> m ())
-> (Tip block)
-> m (LSQ.ClientStAcquired block (Point block) (Ledger.Query block) m ())
-> LSQ.ClientStAcquired block (Point block) (Ledger.Query block) m ()
Expand All @@ -321,17 +329,22 @@ newTimeInterpreterClient = do
let slot = case tip of
TipGenesis -> 0
Tip sl _ _ -> sl
case interpreter `interpretQuery` slotToWallclock slot of
let result = (,)
<$> (interpreter `interpretQuery` slotToWallclock slot)
<*> (interpreter `interpretQuery` slotToEpoch' slot)
case result of
-- NOTE: This request cannot fail in theory because the tip
-- is always known of the interpreter. If that every happens
-- because of some weird condition, retrying should do.
Left{} ->
pure (clientStQuerySlotTime notifyResult tip continue)
Right (slotTime, _) -> do
Right ((slotTime, _), (epochNo, SlotInEpoch -> slotInEpoch)) -> do
NetworkParameters{systemStart} <- asks (view typed)
now <- getCurrentTime
let networkSync = mkNetworkSynchronization systemStart now slotTime
pure (clientStQueryCurrentEra (notifyResult now networkSync) continue)
pure $ clientStQueryCurrentEra
(notifyResult now networkSync (epochNo, slotInEpoch))
continue
}

clientStQueryCurrentEra
Expand Down
15 changes: 15 additions & 0 deletions server/src/Ogmios/Data/Health.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE UndecidableInstances #-}

module Ogmios.Data.Health
Expand All @@ -12,6 +13,8 @@ module Ogmios.Data.Health
, CardanoEra (..)
, ConnectionStatus (..)
, Tip (..)
, SlotInEpoch (..)
, EpochNo (..)
, emptyHealth
, modifyHealth
-- ** NetworkSynchronization
Expand All @@ -28,6 +31,8 @@ import Ogmios.Control.MonadSTM
import Ogmios.Data.Metrics
( Metrics, emptyMetrics )

import Cardano.Slotting.Slot
( EpochNo (..) )
import Data.Aeson
( ToJSON (..), genericToEncoding )
import Data.ByteString.Builder.Scientific
Expand Down Expand Up @@ -77,6 +82,10 @@ data Health block = Health
-- ^ Application metrics measured at regular interval.
, connectionStatus :: !ConnectionStatus
-- ^ State of the connectino with the underlying node.
, currentEpoch :: !(Maybe EpochNo)
-- ^ Current known epoch number
, slotInEpoch :: !(Maybe SlotInEpoch)
-- ^ Relative slot number within the epoch
} deriving stock (Generic, Eq, Show)

instance ToJSON (Tip block) => ToJSON (Health block) where
Expand All @@ -91,6 +100,8 @@ emptyHealth startTime = Health
, currentEra = empty
, metrics = emptyMetrics
, connectionStatus = Disconnected
, currentEpoch = empty
, slotInEpoch = empty
}

modifyHealth
Expand All @@ -105,6 +116,10 @@ modifyHealth tvar fn =
health <- fn <$> readTVar tvar
health <$ writeTVar tvar health

newtype SlotInEpoch = SlotInEpoch
{ getSlotInEpoch :: Word64
} deriving stock (Generic, Eq, Show)
deriving newtype (ToJSON)

-- | Captures how far is our underlying node from the network, in percentage.
-- This is calculated using:
Expand Down
2 changes: 2 additions & 0 deletions server/test/unit/Ogmios/Data/HealthSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ spec = parallel $ do
currentEra health `shouldBe` Nothing
metrics health `shouldBe` emptyMetrics
connectionStatus health `shouldBe` Disconnected
currentEpoch health `shouldBe` Nothing
slotInEpoch health `shouldBe` Nothing

context "NetworkSynchronization" $ do
let matrix =
Expand Down