@@ -5,7 +5,7 @@ module Bench.EndToEnd where
55import Hydra.Prelude
66import Test.Hydra.Prelude
77
8- import Bench.Summary (Summary (.. ), makeQuantiles )
8+ import Bench.Summary (Summary (.. ), SystemStats , makeQuantiles )
99import CardanoClient (RunningNode (.. ), awaitTransaction , submitTransaction , submitTx )
1010import CardanoNode (findRunningCardanoNode' , withCardanoNodeDevnet )
1111import Control.Concurrent.Class.MonadSTM (
@@ -17,6 +17,7 @@ import Control.Concurrent.Class.MonadSTM (
1717 newTVarIO ,
1818 tryReadTBQueue ,
1919 writeTBQueue ,
20+ writeTVar ,
2021 )
2122import Control.Lens (to , (^..) , (^?) )
2223import Control.Monad.Class.MonadAsync (mapConcurrently )
@@ -27,6 +28,7 @@ import Data.Map qualified as Map
2728import Data.Scientific (Scientific )
2829import Data.Set ((\\) )
2930import Data.Set qualified as Set
31+ import Data.Text (pack )
3032import Data.Time (UTCTime (UTCTime ), utctDayTime )
3133import Hydra.Cardano.Api (NetworkId , SocketPath , Tx , TxId , UTxO , getVerificationKey , signTx )
3234import Hydra.Cluster.Faucet (FaucetLog (.. ), publishHydraScriptsAs , returnFundsToFaucet' , seedFromFaucet )
@@ -60,7 +62,7 @@ import HydraNode (
6062 )
6163import System.Directory (findExecutable )
6264import System.FilePath ((</>) )
63- import System.IO (hGetLine , hPutStrLn )
65+ import System.IO (hGetLine )
6466import System.Process (
6567 CreateProcess (.. ),
6668 StdStream (CreatePipe ),
@@ -70,9 +72,8 @@ import System.Process (
7072import Test.HUnit.Lang (formatFailureReason )
7173import Text.Printf (printf )
7274import Text.Regex.TDFA (getAllTextMatches , (=~) )
73- import Prelude (read )
7475
75- bench :: Int -> NominalDiffTime -> FilePath -> Dataset -> IO Summary
76+ bench :: Int -> NominalDiffTime -> FilePath -> Dataset -> IO ( Summary , SystemStats )
7677bench startingNodeId timeoutSeconds workDir dataset = do
7778 putStrLn $ " Test logs available in: " <> (workDir </> " test.log" )
7879 withFile (workDir </> " test.log" ) ReadWriteMode $ \ hdl ->
@@ -81,7 +82,8 @@ bench startingNodeId timeoutSeconds workDir dataset = do
8182 putTextLn " Starting benchmark"
8283 let cardanoKeys = hydraNodeKeys dataset <&> \ sk -> (getVerificationKey sk, sk)
8384 let hydraKeys = generateSigningKey . show <$> [1 .. toInteger (length cardanoKeys)]
84- withOSStats workDir $
85+ statsTvar <- newTVarIO mempty
86+ scenarioData <- withOSStats workDir statsTvar $
8587 withCardanoNodeDevnet (contramap FromCardanoNode tracer) workDir $ \ node@ RunningNode {nodeSocket} -> do
8688 putTextLn " Seeding network"
8789 seedNetwork node dataset (contramap FromFaucet tracer)
@@ -94,6 +96,8 @@ bench startingNodeId timeoutSeconds workDir dataset = do
9496 withHydraCluster hydraTracer workDir nodeSocket startingNodeId cardanoKeys hydraKeys hydraScriptsTxId contestationPeriod depositDeadline $ \ clients -> do
9597 waitForNodesConnected hydraTracer 20 clients
9698 scenario hydraTracer node workDir dataset clients
99+ systemStats <- readTVarIO statsTvar
100+ pure (scenarioData, systemStats)
97101
98102benchDemo ::
99103 NetworkId ->
@@ -102,13 +106,13 @@ benchDemo ::
102106 [Host ] ->
103107 FilePath ->
104108 Dataset ->
105- IO Summary
109+ IO ( Summary , SystemStats )
106110benchDemo networkId nodeSocket timeoutSeconds hydraClients workDir dataset@ Dataset {clientDatasets} = do
107111 putStrLn $ " Test logs available in: " <> (workDir </> " test.log" )
108112 withFile (workDir </> " test.log" ) ReadWriteMode $ \ hdl ->
109113 withTracerOutputTo hdl " Test" $ \ tracer ->
110114 failAfter timeoutSeconds $ do
111- putTextLn " Starting benchmark"
115+ putTextLn " Starting benchmark demo "
112116 let cardanoTracer = contramap FromCardanoNode tracer
113117 findRunningCardanoNode' cardanoTracer networkId nodeSocket >>= \ case
114118 Nothing ->
@@ -122,7 +126,7 @@ benchDemo networkId nodeSocket timeoutSeconds hydraClients workDir dataset@Datas
122126 withHydraClientConnections hydraTracer (hydraClients `zip` [1 .. ]) [] $ \ case
123127 [] -> error " no hydra clients provided"
124128 (leader : followers) ->
125- scenario hydraTracer node workDir dataset (leader :| followers)
129+ (, [] ) <$> scenario hydraTracer node workDir dataset (leader :| followers)
126130 where
127131 withHydraClientConnections tracer apiHosts connections action = do
128132 case apiHosts of
@@ -221,47 +225,67 @@ defaultDescription = ""
221225-- __NOTE__: This function relies on [dstat](https://linux.die.net/man/1/dstat). If the executable is not in the @PATH@
222226-- it's basically a no-op.
223227--
224- -- Writes a @system.csv@ file into given `workDir ` containing one line every 5 second with share of user CPU load.
228+ -- Writes into given `TVar ` containing one line every 5 second with share of user/free memory load.
225229-- Here is a sample content:
226230--
227231-- @@
228- -- 2022-02-16 14:25:43.67203351 UTC,11
229- -- 2022-02-16 14:25:48.669817664 UTC,10
230- -- 2022-02-16 14:25:53.672050421 UTC,14
231- -- 2022-02-16 14:25:58.670460796 UTC,12
232- -- 2022-02-16 14:26:03.669831775 UTC,11
233- -- 2022-02-16 14:26:08.67203726 UTC,10
232+ -- 2025-02-04 17:22:50.30543862 UTC
233+ -- Used: 7648M, Free: 46.9G
234+ -- 2025-02-04 17:22:55.305513945 UTC
235+ -- Used: 7713M, Free: 46.9G
236+ -- 2025-02-04 17:23:00.30550915 UTC
237+ -- Used: 7715M, Free: 46.9G
238+ -- 2025-02-04 17:23:05.305513574 UTC
239+ -- Used: 7717M, Free: 46.8G
240+ -- 2025-02-04 17:23:10.305538265 UTC
241+ -- Used: 7718M, Free: 46.8G
242+ -- 2025-02-04 17:23:15.305519942 UTC
243+ -- Used: 7719M, Free: 46.8G
244+ -- 2025-02-04 17:23:20.30550604 UTC
245+ -- Used: 7722M, Free: 46.8G
246+ -- 2025-02-04 17:23:25.305413146 UTC
247+ -- Used: 7723M, Free: 46.8G
234248-- ...
235249-- @@
236250--
237251-- TODO: add more data points for memory and network consumption
238- withOSStats :: FilePath -> IO a -> IO a
239- withOSStats workDir action =
252+ withOSStats :: FilePath -> TVar IO SystemStats -> IO a -> IO a
253+ withOSStats workDir tvar action =
240254 findExecutable " dstat" >>= \ case
241255 Nothing -> action
242- Just exePath ->
243- withCreateProcess ( process exePath) {std_out = CreatePipe } $ \ _stdin out _stderr _processHandle ->
256+ Just _ ->
257+ withCreateProcess process{std_out = CreatePipe } $ \ _stdin out _stderr _processHandle ->
244258 race
245- (collectStats out $ workDir </> " system.csv " )
259+ (collectStats tvar out )
246260 action
247261 >>= \ case
248- Left () -> failure " dstat process failed unexpectedly"
262+ Left _ -> failure " dstat process failed unexpectedly"
249263 Right a -> pure a
250264 where
251- process exePath = (proc exePath [" -cm" , " -n" , " -N" , " lo" , " --integer " , " --noheaders" , " --noupdate" , " 5" ]){cwd = Just workDir}
252-
253- collectStats Nothing _ = pure ()
254- collectStats (Just hdl) filepath =
255- withFile filepath WriteMode $ \ file ->
256- forever $
257- hGetLine hdl >>= processStat file
258-
259- processStat :: Handle -> String -> IO ()
260- processStat file stat =
261- case getAllTextMatches (stat =~ ( " [0-9]+ " :: String )) :: [ String ] of
262- (cpu : _) -> do
265+ process = (proc " dstat " [" -cm" , " -n" , " -N" , " lo" , " --noheaders" , " --noupdate" , " 5" ]){cwd = Just workDir}
266+
267+ collectStats _ Nothing = pure ()
268+ collectStats tvar' (Just hdl) =
269+ forever $
270+ hGetLine hdl >>= processStat tvar'
271+
272+ processStat :: TVar IO [ Text ] -> String -> IO ()
273+ processStat tvar' stat = do
274+ let matches = getAllTextMatches ( stat =~ ( " [0-9.]+.|([A-Z]) " :: String )) :: [ String ]
275+ case matches of
276+ (_ : _ : _ : _ : _ : memUsed : memFree : _) -> do
263277 now <- getCurrentTime
264- hPutStrLn file $ show now <> " ," <> show ((read cpu :: Double ) / 100 )
278+ let str =
279+ pack $
280+ show now
281+ <> " \n\t "
282+ <> " Used: "
283+ <> memUsed
284+ <> " , "
285+ <> " Free: "
286+ <> memFree
287+ stats <- readTVarIO tvar'
288+ atomically $ writeTVar tvar' $ stats <> [str]
265289 _ -> pure ()
266290
267291-- | Compute average confirmation/validation time over intervals of 5 seconds.
0 commit comments