mirror of
https://github.com/simplex-chat/simplexmq.git
synced 2026-05-14 20:35:08 +00:00
102 lines
3.1 KiB
Haskell
102 lines
3.1 KiB
Haskell
{-# LANGUAGE DeriveGeneric #-}
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
|
{-# LANGUAGE StrictData #-}
|
|
|
|
module Simplex.Messaging.Agent.Store.SQLite.DB
|
|
( Connection (..),
|
|
SlowQueryStats (..),
|
|
open,
|
|
close,
|
|
execute,
|
|
execute_,
|
|
executeNamed,
|
|
executeMany,
|
|
query,
|
|
query_,
|
|
queryNamed,
|
|
)
|
|
where
|
|
|
|
import Control.Concurrent.STM
|
|
import Control.Monad (when)
|
|
import Data.Aeson (ToJSON (..))
|
|
import qualified Data.Aeson as J
|
|
import Data.Int (Int64)
|
|
import Data.Time (diffUTCTime, getCurrentTime)
|
|
import Database.SQLite.Simple (FromRow, NamedParam, Query, ToRow)
|
|
import qualified Database.SQLite.Simple as SQL
|
|
import GHC.Generics (Generic)
|
|
import Simplex.Messaging.TMap (TMap)
|
|
import qualified Simplex.Messaging.TMap as TM
|
|
import Simplex.Messaging.Util (diffToMilliseconds)
|
|
|
|
data Connection = Connection
|
|
{ conn :: SQL.Connection,
|
|
slow :: TMap Query SlowQueryStats
|
|
}
|
|
|
|
data SlowQueryStats = SlowQueryStats
|
|
{ count :: Int64,
|
|
timeMax :: Int64,
|
|
timeAvg :: Int64
|
|
}
|
|
deriving (Show, Generic)
|
|
|
|
instance ToJSON SlowQueryStats where toEncoding = J.genericToEncoding J.defaultOptions
|
|
|
|
timeIt :: TMap Query SlowQueryStats -> Query -> IO a -> IO a
|
|
timeIt slow sql a = do
|
|
t <- getCurrentTime
|
|
r <- a
|
|
t' <- getCurrentTime
|
|
let diff = diffToMilliseconds $ diffUTCTime t' t
|
|
atomically $ when (diff > 50) $ TM.alter (updateQueryStats diff) sql slow
|
|
pure r
|
|
where
|
|
updateQueryStats :: Int64 -> Maybe SlowQueryStats -> Maybe SlowQueryStats
|
|
updateQueryStats diff Nothing = Just $ SlowQueryStats 1 diff diff
|
|
updateQueryStats diff (Just SlowQueryStats {count, timeMax, timeAvg}) =
|
|
Just $
|
|
SlowQueryStats
|
|
{ count = count + 1,
|
|
timeMax = max timeMax diff,
|
|
timeAvg = (timeAvg * count + diff) `div` (count + 1)
|
|
}
|
|
|
|
open :: String -> IO Connection
|
|
open f = do
|
|
conn <- SQL.open f
|
|
slow <- atomically $ TM.empty
|
|
pure Connection {conn, slow}
|
|
|
|
close :: Connection -> IO ()
|
|
close = SQL.close . conn
|
|
|
|
execute :: ToRow q => Connection -> Query -> q -> IO ()
|
|
execute Connection {conn, slow} sql = timeIt slow sql . SQL.execute conn sql
|
|
{-# INLINE execute #-}
|
|
|
|
execute_ :: Connection -> Query -> IO ()
|
|
execute_ Connection {conn, slow} sql = timeIt slow sql $ SQL.execute_ conn sql
|
|
{-# INLINE execute_ #-}
|
|
|
|
executeNamed :: Connection -> Query -> [NamedParam] -> IO ()
|
|
executeNamed Connection {conn, slow} sql = timeIt slow sql . SQL.executeNamed conn sql
|
|
{-# INLINE executeNamed #-}
|
|
|
|
executeMany :: ToRow q => Connection -> Query -> [q] -> IO ()
|
|
executeMany Connection {conn, slow} sql = timeIt slow sql . SQL.executeMany conn sql
|
|
{-# INLINE executeMany #-}
|
|
|
|
query :: (ToRow q, FromRow r) => Connection -> Query -> q -> IO [r]
|
|
query Connection {conn, slow} sql = timeIt slow sql . SQL.query conn sql
|
|
{-# INLINE query #-}
|
|
|
|
query_ :: FromRow r => Connection -> Query -> IO [r]
|
|
query_ Connection {conn, slow} sql = timeIt slow sql $ SQL.query_ conn sql
|
|
{-# INLINE query_ #-}
|
|
|
|
queryNamed :: FromRow r => Connection -> Query -> [NamedParam] -> IO [r]
|
|
queryNamed Connection {conn, slow} sql = timeIt slow sql . SQL.queryNamed conn sql
|
|
{-# INLINE queryNamed #-}
|