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 28, 2024
1 parent ef5f0a9 commit 9a08d29
Show file tree
Hide file tree
Showing 12 changed files with 870 additions and 154 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
170 changes: 170 additions & 0 deletions bench/locli/app/locli-db.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
{-# 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.Concurrent.Async
import Control.Concurrent.Extra
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 qualified Data.Map.Lazy as ML
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/createDbCounting" $ withDb dbName $ do
let
alterFunc :: Maybe Int -> Maybe Int
alterFunc = maybe (Just 1) (Just . succ)

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

mapM_ runSqlRunnable (logObjectToSql logObject)

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

mapM_ run createTables

tracefreqs <- transaction $ foldM go (ML.empty :: TraceFreqs) ls

transaction $ mapM_ runSqlRunnable (traceFreqsToSql tracefreqs)

[[tMin, tMax]] <- run "SELECT MIN(at), MAX(at) FROM (SELECT at FROM resource union SELECT at FROM slot union SELECT at FROM txns union SELECT at FROM event)"
let
dbSummary = SummaryDB
{ sdbName = "test"
, sdbLines = sum tracefreqs
, sdbFirstAt = fromSqlData tMin
, sdbLastAt = fromSqlData tMax
}
runSqlRunnable $ summaryToSql dbSummary

pure ()


dbSummaryDum :: SummaryDB
dbSummaryDum = SummaryDB "tryRead" 0 undefined undefined

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 dbSummaryDum 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 tracefreq (msg TEXT NOT NULL, count INTEGER NOT NULL)"
, "CREATE TABLE summary (name TEXT NOT NULL, lines INTEGER NOT NULL, first_at REAL NOT NULL, last_at REAL NOT NULL)"
, "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
-}
31 changes: 30 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,31 @@ 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
, async
, bytestring
, containers
, cardano-api
, directory
, extra
, split
, text
, text-short
, time
, trace-resources
, sqlite-easy >= 1.1.0.1
, unordered-containers

test-suite test-locli
import: project-config

Expand All @@ -163,4 +191,5 @@ test-suite test-locli
, text

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 9a08d29

Please sign in to comment.