Skip to content

Commit b2b5692

Browse files
authored
Merge pull request #6473 from IntersectMBO/russoul/integrate-timeseries-io
bench: Integrate cardano-timeseries-io
2 parents a9bf266 + 11fe67c commit b2b5692

19 files changed

Lines changed: 338 additions & 92 deletions

File tree

bench/cardano-timeseries-io/src/Cardano/Timeseries/Component.hs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
{-# HLINT ignore "Use newtype instead of data" #-}
55
{-# LANGUAGE AllowAmbiguousTypes #-}
66
{-# LANGUAGE FlexibleContexts #-}
7-
{-# LANGUAGE NumericUnderscores #-}
87
{-# LANGUAGE RecordWildCards #-}
98
{-# LANGUAGE ScopedTypeVariables #-}
109
{-# LANGUAGE TypeApplications #-}
@@ -81,16 +80,16 @@ create tr mbCfg = do
8180
runPruner handle = threadLabelMe "timeseries-pruner-thread" >> do
8281
forever $ do
8382
cfg <- readTVarIO handle.config
84-
case cfg.pruningPeriodSec of
83+
case cfg.pruningPeriodMillis of
8584
Nothing ->
8685
-- If the current configuration doesn't specify a pruning period, we block
8786
-- the thread until a reconfiguration happens.
8887
takeMVar handle.reconfigured
89-
Just period -> do
88+
Just periodMs -> do
9089
prune handle
9190
-- Wait for the given period or wake up on a reconfiguration.
9291
race_
93-
(threadDelay (fromIntegral period * 1_000_000))
92+
(threadDelay (fromIntegral periodMs * 1000))
9493
(takeMVar handle.reconfigured)
9594

9695
-- | Reconfigure the store. The new parameters are applied immediately.

bench/cardano-timeseries-io/src/Cardano/Timeseries/Component/Types.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ type QueryId = Word64
1313
data TimeseriesConfig = TimeseriesConfig {
1414
-- | How long the store entries are retained for (ms).
1515
retentionMillis :: Word64
16-
-- | How often the pruner thread shall prune the store (sec), if enabled.
17-
, pruningPeriodSec :: Maybe Word64
16+
-- | How often the pruner thread shall prune the store (ms), if enabled.
17+
, pruningPeriodMillis :: Maybe Word64
1818
-- | Parameters of timeseries query interpretation.
1919
, interpCfg :: Interp.Config
2020
} deriving (Show, Generic, ToJSON)

cardano-tracer/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# ChangeLog
22

33
## NEXT
4+
5+
## 0.4.0 (April 2026)
46
* RTView: Remove monitoring based on the `NodePeers` datapoint, which has been removed
57
since Node 10.6.2; fixes the RTView-enabled build.
68
* Introduce secure communication via HTTPS for EKG and Prometheus metric servers, enabled
@@ -16,6 +18,10 @@
1618
, "certificateKeyFile": "/path/to/key.pem"
1719
, "certificateChain": ["/path/to/intermediate1.pem", "/path/to/intermediate2.pem"]
1820
}
21+
* Integrate a timeseries store as a REST API:
22+
- Tweak ekg-forward to relay node-emanated metrics to the timeseries store
23+
- The store can be queried, pruned and configured dynamically via the API
24+
- The store can be pruned periodically by an automatic process (configurable)
1925

2026
## 0.3.6 (November 2025)
2127
* Implement Prometheus HTTP service discovery (SD) under the URL `/targets`

cardano-tracer/bench/cardano-tracer-bench.hs

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# LANGUAGE NamedFieldPuns #-}
2+
{-# LANGUAGE OverloadedRecordDot #-}
23
{-# LANGUAGE OverloadedStrings #-}
34

45
import Cardano.Logging hiding (LocalSocket)
@@ -61,7 +62,7 @@ main = do
6162
rtViewPageOpened <- newTVarIO False
6263
#endif
6364

64-
tracer <- mkTracerTracer $ SeverityF $ Just Warning
65+
tracer <- mkTraceBundle $ SeverityF $ Just Warning
6566

6667
let tracerEnv :: TracerConfig -> HandleRegistry -> TracerEnv
6768
tracerEnv config handleRegistry = TracerEnv
@@ -73,11 +74,12 @@ main = do
7374
, teCurrentDPLock = currentDPLock
7475
, teDPRequestors = dpRequestors
7576
, teProtocolsBrake = protocolsBrake
76-
, teTracer = tracer
77+
, teTracer = tracer.assorted
7778
, teReforwardTraceObjects = \_-> pure ()
7879
, teRegistry = handleRegistry
7980
, teStateDir = Nothing
8081
, teMetricsHelp = []
82+
, teTimeseriesHandle = Nothing
8183
}
8284

8385
tracerEnvRTView :: TracerEnvRTView
@@ -140,22 +142,23 @@ main = do
140142

141143
mkConfig :: FilePath -> LogFormat -> TracerConfig
142144
mkConfig root format = TracerConfig
143-
{ networkMagic = 764824073
144-
, network = AcceptAt (Net.LocalPipe "")
145-
, loRequestNum = Nothing
146-
, ekgRequestFreq = Nothing
147-
, hasEKG = Nothing
148-
, hasPrometheus = Nothing
149-
, hasRTView = Nothing
150-
, tlsCertificate = Nothing
151-
, logging = NE.fromList [LoggingParams root FileMode format]
152-
, rotation = Nothing
153-
, verbosity = Nothing
154-
, metricsNoSuffix = Nothing
155-
, metricsHelp = Nothing
156-
, hasForwarding = Nothing
157-
, resourceFreq = Nothing
158-
, ekgRequestFull = Nothing
145+
{ networkMagic = 764824073
146+
, network = AcceptAt (Net.LocalPipe "")
147+
, loRequestNum = Nothing
148+
, ekgRequestFreq = Nothing
149+
, hasEKG = Nothing
150+
, hasPrometheus = Nothing
151+
, hasRTView = Nothing
152+
, hasTimeseries = Nothing
153+
, tlsCertificate = Nothing
154+
, logging = NE.fromList [LoggingParams root FileMode format]
155+
, rotation = Nothing
156+
, verbosity = Nothing
157+
, metricsNoSuffix = Nothing
158+
, metricsHelp = Nothing
159+
, hasForwarding = Nothing
160+
, resourceFreq = Nothing
161+
, ekgRequestFull = Nothing
159162
, prometheusLabels = Nothing
160163
}
161164

cardano-tracer/cardano-tracer.cabal

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cabal-version: 3.0
22

33
name: cardano-tracer
4-
version: 0.3.7
4+
version: 0.4.0
55
synopsis: A service for logging and monitoring over Cardano nodes
66
description: A service for logging and monitoring over Cardano nodes.
77
category: Cardano,
@@ -123,6 +123,7 @@ library
123123
Cardano.Tracer.Handlers.Metrics.Monitoring
124124
Cardano.Tracer.Handlers.Metrics.Prometheus
125125
Cardano.Tracer.Handlers.Metrics.Servers
126+
Cardano.Tracer.Handlers.Metrics.TimeseriesServer
126127
Cardano.Tracer.Handlers.Metrics.Utils
127128

128129
Cardano.Tracer.Handlers.Notifications.Check
@@ -147,6 +148,8 @@ library
147148

148149
other-modules: Cardano.Tracer.Handlers.Logs.Journal.NoSystemd
149150
Cardano.Tracer.Handlers.Notifications.Timer
151+
Cardano.Tracer.Time
152+
150153
Paths_cardano_tracer
151154

152155
autogen-modules: Paths_cardano_tracer
@@ -155,6 +158,7 @@ library
155158
build-depends:
156159
cardano-git-rev ^>=0.2.2
157160
, cassava
161+
, string-qq
158162
, threepenny-gui
159163
, utf8-string
160164
, vector
@@ -201,6 +205,7 @@ library
201205
, warp ^>= 3.4
202206
, warp-tls
203207
, yaml
208+
, cardano-timeseries-io
204209

205210
if os(windows)
206211
build-depends: Win32

cardano-tracer/src/Cardano/Tracer/Acceptors/Client.hs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import Data.Word (Word32)
4141
import qualified Network.Mux as Mux
4242
import qualified Network.Socket as Socket
4343
import qualified System.Metrics.Configuration as EKGF
44-
import System.Metrics.Network.Acceptor (acceptEKGMetricsInit)
44+
import System.Metrics.Network.Acceptor (acceptMetricsInit)
4545

4646
import qualified Trace.Forward.Configuration.DataPoint as DPF
4747
import qualified Trace.Forward.Configuration.TraceObject as TF
@@ -192,9 +192,10 @@ runEKGAcceptorInit
192192
respoinderCtx
193193
LBS.ByteString IO () Void
194194
runEKGAcceptorInit tracerEnv ekgConfig errorHandler =
195-
acceptEKGMetricsInit
195+
acceptMetricsInit
196196
ekgConfig
197197
(prepareMetricsStores tracerEnv . micConnectionId)
198+
(store tracerEnv . connIdToNodeId . micConnectionId)
198199
(errorHandler . micConnectionId)
199200

200201
runTraceObjectsAcceptorInit

cardano-tracer/src/Cardano/Tracer/Acceptors/Server.hs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ module Cardano.Tracer.Acceptors.Server
55
( runAcceptorsServer
66
) where
77

8-
import "contra-tracer" Control.Tracer (nullTracer)
9-
108
import Cardano.Logging (TraceObject)
119
import qualified Cardano.Logging.Types as Net
1210
import Cardano.Tracer.Acceptors.Utils
@@ -25,25 +23,25 @@ import Ouroboros.Network.Mux (MiniProtocol (..), MiniProtocolLimits (.
2523
miniProtocolNum, miniProtocolRun)
2624
import Ouroboros.Network.Protocol.Handshake (Handshake, HandshakeArguments (..))
2725
import qualified Ouroboros.Network.Protocol.Handshake as Handshake
26+
import qualified Ouroboros.Network.Server.Simple as Server
2827
import Ouroboros.Network.Snocket (LocalAddress, LocalSocket, Snocket,
2928
localAddressFromPath, localSnocket, makeLocalBearer, makeSocketBearer,
3029
socketSnocket)
31-
import Ouroboros.Network.Socket (ConnectionId (..),
32-
SomeResponderApplication (..))
33-
import qualified Ouroboros.Network.Server.Simple as Server
30+
import Ouroboros.Network.Socket (ConnectionId (..), SomeResponderApplication (..))
3431

3532
import Codec.CBOR.Term (Term)
3633
import Control.Concurrent.Async (wait)
34+
import "contra-tracer" Control.Tracer (nullTracer)
3735
import qualified Data.ByteString.Lazy as LBS
36+
import Data.Functor (void)
3837
import Data.List.NonEmpty (NonEmpty ((:|)))
3938
import qualified Data.Text as Text
40-
import Data.Functor (void)
4139
import Data.Void (Void)
4240
import Data.Word (Word32)
4341
import qualified Network.Mux as Mux
4442
import qualified Network.Socket as Socket
4543
import qualified System.Metrics.Configuration as EKGF
46-
import System.Metrics.Network.Acceptor (acceptEKGMetricsResp)
44+
import System.Metrics.Network.Acceptor (acceptMetricsResp)
4745

4846
import qualified Trace.Forward.Configuration.DataPoint as DPF
4947
import qualified Trace.Forward.Configuration.TraceObject as TF
@@ -184,9 +182,10 @@ runEKGAcceptor
184182
-> (ConnectionId addr -> IO ())
185183
-> RunMiniProtocol 'Mux.ResponderMode initiatorCtx (ResponderContext addr) LBS.ByteString IO Void ()
186184
runEKGAcceptor tracerEnv ekgConfig errorHandler =
187-
acceptEKGMetricsResp
185+
acceptMetricsResp
188186
ekgConfig
189187
(prepareMetricsStores tracerEnv . rcConnectionId)
188+
(store tracerEnv . connIdToNodeId . rcConnectionId)
190189
(errorHandler . rcConnectionId)
191190

192191
runTraceObjectsAcceptor

cardano-tracer/src/Cardano/Tracer/Acceptors/Utils.hs

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{-# LANGUAGE NamedFieldPuns #-}
22
{-# LANGUAGE OverloadedStrings #-}
33
{-# LANGUAGE TupleSections #-}
4+
{-# LANGUAGE ViewPatterns #-}
45

56
{-# OPTIONS_GHC -Wno-redundant-constraints #-}
67

@@ -9,31 +10,41 @@ module Cardano.Tracer.Acceptors.Utils
910
, prepareMetricsStores
1011
, removeDisconnectedNode
1112
, notifyAboutNodeDisconnected
13+
, store
1214
) where
1315

1416
#if RTVIEW
1517
import Cardano.Logging (SeverityS (..))
18+
#endif
19+
import qualified Cardano.Timeseries.Component as Timeseries
20+
import Cardano.Timeseries.Domain.Types (MetricIdentifier)
21+
import Cardano.Tracer.Environment
22+
#if RTVIEW
1623
import Cardano.Tracer.Handlers.Notifications.Types
1724
import Cardano.Tracer.Handlers.Notifications.Utils
1825
#endif
19-
import Cardano.Tracer.Environment
26+
import Cardano.Tracer.Time (getTimeMs)
2027
import Cardano.Tracer.Types
2128
import Cardano.Tracer.Utils
2229
import Ouroboros.Network.Socket (ConnectionId (..))
2330

2431
import Control.Concurrent.STM (atomically)
2532
import Control.Concurrent.STM.TVar (TVar, modifyTVar', newTVarIO)
2633
import qualified Data.Bimap as BM
34+
import Data.Foldable
2735
import qualified Data.Map.Strict as M
36+
import Data.Maybe (mapMaybe)
2837
import qualified Data.Set as S
29-
import Data.Time.Clock.POSIX (getPOSIXTime)
3038
#if RTVIEW
3139
import Data.Time.Clock.System (getSystemTime, systemToUTCTime)
3240
#endif
3341
import qualified System.Metrics as EKG
34-
import System.Metrics.Store.Acceptor (MetricsLocalStore, emptyMetricsLocalStore)
42+
import System.Metrics.ReqResp
43+
import System.Metrics.Store.Acceptor (MetricsLocalStore, emptyMetricsLocalStore,
44+
storeMetrics)
3545

3646
import Trace.Forward.Utils.DataPoint (DataPointRequestor, initDataPointRequestor)
47+
import qualified Data.Text.Read as Text
3748

3849
prepareDataPointRequestor
3950
:: Show addr
@@ -54,26 +65,17 @@ prepareMetricsStores
5465
-> IO (EKG.Store, TVar MetricsLocalStore)
5566
prepareMetricsStores TracerEnv{teConnectedNodes, teAcceptedMetrics} connId = do
5667
addConnectedNode teConnectedNodes connId
57-
store <- EKG.newStore
68+
st <- EKG.newStore
5869

59-
EKG.registerCounter "ekg.server_timestamp_ms" getTimeMs store
60-
storesForNewNode <- (store ,) <$> newTVarIO emptyMetricsLocalStore
70+
EKG.registerCounter "ekg.server_timestamp_ms" getTimeMs st
71+
storesForNewNode <- (st ,) <$> newTVarIO emptyMetricsLocalStore
6172

6273
atomically do
6374
modifyTVar' teAcceptedMetrics do
6475
M.insert (connIdToNodeId connId) storesForNewNode
6576

6677
return storesForNewNode
6778

68-
where
69-
-- forkServer definition of `getTimeMs'. The ekg frontend relies
70-
-- on the "ekg.server_timestamp_ms" metric being in every
71-
-- store. While forkServer adds that that automatically we must
72-
-- manually add it.
73-
-- url
74-
-- + https://github.com/tvh/ekg-wai/blob/master/System/Remote/Monitoring/Wai.hs#L237-L238
75-
getTimeMs = (round . (* 1000)) `fmap` getPOSIXTime
76-
7779
addConnectedNode
7880
:: Show addr
7981
=> ConnectedNodes
@@ -115,3 +117,23 @@ notifyAboutNodeDisconnected TracerEnvRTView{teEventsQueues} connId = do
115117
#else
116118
notifyAboutNodeDisconnected _ _ = pure ()
117119
#endif
120+
121+
store :: TracerEnv -> NodeId -> (EKG.Store, TVar MetricsLocalStore) -> Response -> IO ()
122+
store tracerEnv (NodeId nodeId) (ekgStore, localStore) resp@(ResponseMetrics ms) = do
123+
storeMetrics resp ekgStore localStore
124+
for_ (teTimeseriesHandle tracerEnv) $ \h -> do
125+
ts <- getTimeMs
126+
Timeseries.insert h "node_id" nodeId (fromIntegral ts) (mapMaybe parseMetric ms)
127+
128+
where
129+
numeralOnly :: MetricValue -> Maybe Double
130+
numeralOnly (GaugeValue x) = Just (fromIntegral x)
131+
numeralOnly (CounterValue x) = Just (fromIntegral x)
132+
-- If the label is readable as double, accept it
133+
numeralOnly (LabelValue (Text.double -> Right (x, ""))) = Just x
134+
numeralOnly _ = Nothing
135+
136+
parseMetric :: (MetricName, MetricValue) -> Maybe (MetricIdentifier, Double)
137+
parseMetric (k, numeralOnly -> Just v) = Just (k, v)
138+
parseMetric _ = Nothing
139+

cardano-tracer/src/Cardano/Tracer/Configuration.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ data TracerConfig = TracerConfig
164164
, hasEKG :: !(Maybe Endpoint) -- ^ Endpoint for EKG web-page.
165165
, hasPrometheus :: !(Maybe Endpoint) -- ^ Endpoint for Prometheus web-page.
166166
, hasRTView :: !(Maybe Endpoint) -- ^ Endpoint for RTView web-page.
167+
, hasTimeseries :: !(Maybe Endpoint)
167168
, tlsCertificate :: !(Maybe Certificate)
168169
-- | Socket for tracer's to reforward on. Second member of the triplet is the list of prefixes to reforward.
169170
-- Third member of the triplet is the forwarder config.

cardano-tracer/src/Cardano/Tracer/Environment.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module Cardano.Tracer.Environment
66
) where
77

88
import Cardano.Logging.Types
9+
import Cardano.Timeseries.Component (TimeseriesHandle)
910
import Cardano.Tracer.Configuration
1011
#if RTVIEW
1112
import Cardano.Tracer.Handlers.Notifications.Types
@@ -36,6 +37,7 @@ data TracerEnv = TracerEnv
3637
, teRegistry :: !HandleRegistry
3738
, teStateDir :: !(Maybe FilePath)
3839
, teMetricsHelp :: ![(Text, Builder)]
40+
, teTimeseriesHandle :: !(Maybe TimeseriesHandle)
3941
}
4042

4143
#if RTVIEW

0 commit comments

Comments
 (0)