Skip to content

Commit

Permalink
WIP: db persistence layer for locli
Browse files Browse the repository at this point in the history
  • Loading branch information
mgmeier committed Nov 27, 2024
1 parent ef5f0a9 commit db5575a
Show file tree
Hide file tree
Showing 12 changed files with 778 additions and 146 deletions.
4 changes: 4 additions & 0 deletions bench/locli/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Revision history for locli

## NEXT

* New database persistence backend for log objects

## 1.36 -- Nov 2024

* Add `CHANGELOG.md` for `locli`
Expand Down
161 changes: 161 additions & 0 deletions bench/locli/app/locli-db.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
{-# OPTIONS_GHC -fno-warn-unused-top-binds #-}
{-# OPTIONS_GHC -fno-warn-unused-imports #-}
{-# OPTIONS_GHC -fno-warn-unused-local-binds #-}
{-# OPTIONS_GHC -fno-warn-incomplete-uni-patterns #-}


import Cardano.Api (BlockNo (..), MonadIO, SlotNo (..))

import Cardano.Analysis.API.Ground (Hash (..), Host (..), TId (..))
import Cardano.Logging.Resources.Types (ResourceStats, Resources (..))
import Cardano.Unlog.LogObject (LOAnyType (..), LOBody (..), LogObject (..), fromTextRef)
import Cardano.Unlog.LogObjectDB
import Cardano.Util (smaybe)

import Prelude hiding (log)

import Control.Exception
import Control.Monad
import Data.Aeson as Aeson
import Data.Bool (bool)
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.Coerce
import Data.Data
import qualified Data.HashMap.Lazy as HM
import qualified Data.HashMap.Strict as HMS
import Data.List.Split (chop)
import Data.Maybe
import qualified Data.Text as TS (Text, empty, intercalate, pack, splitOn, unpack)
import qualified Data.Text.Lazy as TL (Text, fromStrict, pack)
import qualified Data.Text.Short as ShortText (ShortText, empty, fromText, pack, toText)
import Data.Time.Clock (UTCTime)
import Data.Time.Clock.POSIX (getPOSIXTime, posixSecondsToUTCTime, utcTimeToPOSIXSeconds)
import Data.Typeable
import Data.Word
import qualified GHC.Stats as RTS
import System.Directory (removeFile)
import System.Environment (getArgs)

import Database.Sqlite.Easy hiding (Text)


dbName :: ConnectionString
dbName = "test_logobject.sqlite3"

dbPath :: FilePath
dbPath = TS.unpack $ unConnectionString dbName

main :: IO ()
main = do
print selectAll
getArgs >>= \case
[] -> tryRead >> putStrLn "(to recreate DB, provide log name as arg)"
log : _ -> do
removeFile dbPath `catch` \SomeException{} -> pure ()
ls <- BSL.lines <$> BSL.readFile log

{-
timed "withDb/createDb" $ withDb dbName $ do
mapM_ run createTables
transaction $
sequence_ [ statement `runWith` args | Just (statement, args) <- map logLineToSQL ls ]
pure ()
-}

counts <-
timed "withDb/createDbCounting" $ withDb dbName $ do
let
alterFunc :: Maybe Int -> Maybe Int
alterFunc = maybe (Just 1) (Just . succ)

countMap :: HM.HashMap ShortText.ShortText Int
countMap = HM.empty

go acc line = case eitherDecode line of
Right logObject@LogObject{loNS, loKind} -> do

forM_ (logObjectToSql logObject) $
uncurry runWith
let name = fromTextRef loNS <> ":" <> fromTextRef loKind
pure $ HM.alter alterFunc name acc
Left err -> uncurry runWith (errorToSql err $ BSL.unpack line) >> pure acc

mapM_ run createTables
transaction $ foldM go countMap ls

print counts


tryRead :: IO ()
tryRead = do
res <- take 84 . drop 4096 <$> timed "withDb/selectAll" (withDb dbName (run selectAll))
mapM_ print res
mapM_ (\r -> print r >> print (sqlToLogObject SummaryDB r)) res

timed :: MonadIO m => String -> m a -> m a
timed name action = do
before <- liftIO getPOSIXTime
result <- action
after <- liftIO getPOSIXTime
heap <- liftIO $ RTS.gcdetails_mem_in_use_bytes . RTS.gc <$> RTS.getRTSStats

let
seconds :: Int
seconds = floor $ after - before
mibibytes = heap `div` 1024 `div` 1024

liftIO $ putStrLn $ "<<timed>> time: " ++ show seconds ++ "s; heap: " ++ show mibibytes ++ "MiB; (" ++ name ++ ")"
pure result



createTables :: [SQL]
createTables =
[ "CREATE TABLE event (at REAL NOT NULL, cons TEXT NOT NULL, slot INTEGER, block INTEGER, hash TEXT)"
, "CREATE TABLE resource (at REAL NOT NULL, centi_cpu INTEGER, rss INTEGER, heap INTEGER, alloc INTEGER, as_blob BLOB)"
, "CREATE TABLE slot (at REAL NOT NULL, slot INTEGER, utxo_size INTEGER, chain_dens REAL)"
, "CREATE TABLE txns (at REAL NOT NULL, cons TEXT NOT NULL, count INTEGER, rejected INTEGER, tid TEXT)"
, "CREATE TABLE error (msg TEXT NOT NULL, input TEXT)"
]

selectAll :: SQL
selectAll =
"SELECT at, cons, slot as arg1, block as arg2, null as arg3, hash as arg4 FROM event \
\UNION \
\SELECT at, cons, count as arg1, rejected as arg2, null as arg3, tid as arg4 FROM txns \
\UNION \
\SELECT at, 'LOResources' as cons, null, null, null, as_blob FROM resource \
\UNION \
\SELECT at, 'LOTraceStartLeadershipCheck' as cons, slot, utxo_size, chain_dens, null FROM slot \
\ORDER BY at"


bySlotDomain :: [LogObject] -> [(SlotNo, [LogObject])]
bySlotDomain logObjs =
case dropWhile (isNothing . newSlot) logObjs of
[] -> []
xs -> chop go xs
where
newSlot LogObject{loBody} = case loBody of { LOTraceStartLeadershipCheck s _ _ -> Just s; _ -> Nothing }

go ~(lo:los) = let (inSlot, rest) = span (isNothing . newSlot) los in ((fromJust $ newSlot lo, inSlot), rest)


{-
createIndex :: SQL
createIndex =
"CREATE INDEX idx_lo_body ON lo_body(at)"
-}

{-
mempool saturation example, needs reducer:
select at, null as slot, count from txns where cons='LOMempoolTxs' union select at, slot, null as count from slot order by at;
-}

{-
TODO: gather summary data:
grep '^{' f | head -n1: 1st timestamp
grep -c '^{' f: no. of trace msgs
tail -n1 f: last timestamp
-}
29 changes: 28 additions & 1 deletion bench/locli/locli.cabal
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cabal-version: 3.0

name: locli
version: 1.36
version: 2.0
synopsis: Cardano log analysis CLI
description: Cardano log analysis CLI.
category: Cardano,
Expand Down Expand Up @@ -89,7 +89,9 @@ library
Cardano.Org
Cardano.Render

Cardano.Unlog.BackendFile
Cardano.Unlog.LogObject
Cardano.Unlog.LogObjectDB
Cardano.Unlog.Resources

other-modules: Paths_locli
Expand All @@ -116,6 +118,7 @@ library
, ouroboros-network-api ^>= 0.10
, sop-core
, split
, sqlite-easy >= 1.1.0.1
, statistics
, strict-sop-core
, text
Expand Down Expand Up @@ -147,6 +150,28 @@ executable locli
, transformers
, transformers-except

executable locli-db
import: project-config

hs-source-dirs: app
main-is: locli-db.hs
ghc-options: -threaded
-rtsopts
"-with-rtsopts=-T -N7 -A2m -qb -H64m"

build-depends: locli
, aeson
, bytestring
, cardano-api
, directory
, split
, text
, text-short
, time
, trace-resources
, sqlite-easy >= 1.1.0.1
, unordered-containers

test-suite test-locli
import: project-config

Expand All @@ -161,6 +186,8 @@ test-suite test-locli
, hedgehog-extras ^>= 0.6.4
, locli
, text
, unordered-containers

other-modules: Test.Analysis.CDF
Test.Unlog.LogObjectDB
Test.Unlog.Org
48 changes: 27 additions & 21 deletions bench/locli/src/Cardano/Analysis/API/Ground.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE GeneralisedNewtypeDeriving #-}
{-# LANGUAGE PolyKinds #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module Cardano.Analysis.API.Ground
( module Cardano.Analysis.API.Ground
Expand All @@ -10,28 +11,29 @@ module Cardano.Analysis.API.Ground
)
where

import Prelude as P (show)
import Cardano.Prelude hiding (head, toText)
import Unsafe.Coerce qualified as Unsafe
import Cardano.Prelude hiding (head, toText)
import Cardano.Slotting.Slot (EpochNo (..), SlotNo (..))
import Cardano.Util
import Ouroboros.Network.Block (BlockNo (..))

import Data.Aeson
import Data.Aeson.Types (toJSONKeyText)
import Data.ByteString.Lazy.Char8 qualified as LBS
import Data.Map.Strict qualified as Map
import Data.Text qualified as T
import Data.Text.Short qualified as SText
import Data.Text.Short (ShortText, fromText, toText)
import Data.Time.Clock (UTCTime, NominalDiffTime)
import Options.Applicative
import Options.Applicative qualified as Opt
import System.FilePath qualified as F
import Prelude as P (show)

import Cardano.Slotting.Slot (EpochNo(..), SlotNo(..))
import Ouroboros.Network.Block (BlockNo(..))
import Data.Aeson
import Data.Aeson.Types (toJSONKeyText)
import qualified Data.ByteString.Lazy.Char8 as LBS
import Data.CDF
import Data.Data (Data)
import Data.DataDomain
import qualified Data.Map.Strict as Map
import qualified Data.Text as T
import Data.Text.Short (ShortText, fromText, toText)
import qualified Data.Text.Short as SText
import Data.Time.Clock (NominalDiffTime, UTCTime)
import Options.Applicative
import qualified Options.Applicative as Opt
import qualified System.FilePath as F

import Data.CDF
import Data.DataDomain
import Cardano.Util
import qualified Unsafe.Coerce as Unsafe


newtype FieldName = FieldName { unFieldName :: Text }
Expand All @@ -51,7 +53,7 @@ instance Show TId where
show = ("TId " ++) . P.show . unTId

newtype Hash = Hash { unHash :: ShortText }
deriving (Eq, Generic, Ord)
deriving (Eq, Generic, Ord, Data)
deriving newtype (FromJSON, ToJSON)
deriving anyclass NFData

Expand Down Expand Up @@ -112,6 +114,10 @@ data HostDeduction
= HostFromLogfilename
deriving stock (Eq, Ord, Show)

deriving instance Data SlotNo

deriving instance Data BlockNo

---
--- Files
---
Expand Down
2 changes: 1 addition & 1 deletion bench/locli/src/Cardano/Analysis/MachPerf.hs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ timelineFromLogObjects run@Run{genesis} (f, xs') =
$ foldl' (timelineStep run f) zeroTimelineAccum xs
& (aRunScalars &&& reverse . aSlotStats)
where
xs = filter (not . (`textRefEquals` "DecodeError") . loKind) xs'
xs = filter (not . ("DecodeError" `textRefEquals`) . loKind) xs'

firstRelevantLogObjectTime :: UTCTime
firstRelevantLogObjectTime = loAt (head xs) `max` systemStart genesis
Expand Down
1 change: 1 addition & 0 deletions bench/locli/src/Cardano/Command.hs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import Cardano.Analysis.MachPerf
import Cardano.Analysis.Summary
import Cardano.Render
import Cardano.Report
import Cardano.Unlog.BackendFile
import Cardano.Unlog.LogObject
import Cardano.Util hiding (head)

Expand Down
Loading

0 comments on commit db5575a

Please sign in to comment.