{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE TemplateHaskell #-} module Simplex.Messaging.Agent.Stats ( AgentSMPServerStats (..), AgentSMPServerStatsData (..), OptionalInt (..), AgentXFTPServerStats (..), AgentXFTPServerStatsData (..), AgentNtfServerStats (..), AgentNtfServerStatsData (..), AgentPersistedServerStats (..), OptionalMap (..), newAgentSMPServerStats, newAgentSMPServerStatsData, newAgentSMPServerStats', getAgentSMPServerStats, addSMPStatsData, newAgentXFTPServerStats, newAgentXFTPServerStatsData, newAgentXFTPServerStats', getAgentXFTPServerStats, addXFTPStatsData, newAgentNtfServerStats, newAgentNtfServerStatsData, newAgentNtfServerStats', getAgentNtfServerStats, addNtfStatsData, ) where import Data.Aeson (FromJSON (..), FromJSONKey, ToJSON (..)) import qualified Data.Aeson.TH as J import Data.Int (Int64) import Data.Map.Strict (Map) import qualified Data.Map.Strict as M import Simplex.Messaging.Agent.Protocol (UserId) import Simplex.Messaging.Agent.Store.DB (FromField (..), ToField (..), fromTextField_) import Simplex.Messaging.Parsers (defaultJSON) import Simplex.Messaging.Protocol (NtfServer, SMPServer, XFTPServer) import Simplex.Messaging.Util (decodeJSON, encodeJSON) import UnliftIO.STM data AgentSMPServerStats = AgentSMPServerStats { sentDirect :: TVar Int, -- successfully sent messages sentViaProxy :: TVar Int, -- successfully sent messages via proxy sentProxied :: TVar Int, -- successfully sent messages to other destination server via this as proxy sentDirectAttempts :: TVar Int, -- direct sending attempts (min 1 for each sent message) sentViaProxyAttempts :: TVar Int, -- proxy sending attempts sentProxiedAttempts :: TVar Int, -- attempts sending to other destination server via this as proxy sentAuthErrs :: TVar Int, -- send AUTH errors sentQuotaErrs :: TVar Int, -- send QUOTA permanent errors (message expired) sentExpiredErrs :: TVar Int, -- send expired errors sentOtherErrs :: TVar Int, -- other send permanent errors (excluding above) recvMsgs :: TVar Int, -- total messages received recvDuplicates :: TVar Int, -- duplicate messages received recvCryptoErrs :: TVar Int, -- message decryption errors recvErrs :: TVar Int, -- receive errors ackMsgs :: TVar Int, -- total messages acknowledged ackAttempts :: TVar Int, -- acknowledgement attempts ackNoMsgErrs :: TVar Int, -- NO_MSG ack errors ackOtherErrs :: TVar Int, -- other permanent ack errors (temporary accounted for in attempts) -- conn stats are accounted for rcv queue server connCreated :: TVar Int, -- total connections created connSecured :: TVar Int, -- connections secured connCompleted :: TVar Int, -- connections completed connDeleted :: TVar Int, -- total connections deleted connDelAttempts :: TVar Int, -- total connection deletion attempts connDelErrs :: TVar Int, -- permanent connection deletion errors (temporary accounted for in attempts) connSubscribed :: TVar Int, -- total successful subscription connSubAttempts :: TVar Int, -- subscription attempts connSubIgnored :: TVar Int, -- subscription results ignored (client switched to different session or it was not pending) connSubErrs :: TVar Int, -- permanent subscription errors (temporary accounted for in attempts) -- notifications stats ntfKey :: TVar Int, ntfKeyAttempts :: TVar Int, ntfKeyDeleted :: TVar Int, ntfKeyDeleteAttempts :: TVar Int } data AgentSMPServerStatsData = AgentSMPServerStatsData { _sentDirect :: Int, _sentViaProxy :: Int, _sentProxied :: Int, _sentDirectAttempts :: Int, _sentViaProxyAttempts :: Int, _sentProxiedAttempts :: Int, _sentAuthErrs :: Int, _sentQuotaErrs :: Int, _sentExpiredErrs :: Int, _sentOtherErrs :: Int, _recvMsgs :: Int, _recvDuplicates :: Int, _recvCryptoErrs :: Int, _recvErrs :: Int, _ackMsgs :: Int, _ackAttempts :: Int, _ackNoMsgErrs :: Int, _ackOtherErrs :: Int, _connCreated :: Int, _connSecured :: Int, _connCompleted :: Int, _connDeleted :: Int, _connDelAttempts :: Int, _connDelErrs :: Int, _connSubscribed :: Int, _connSubAttempts :: Int, _connSubIgnored :: Int, _connSubErrs :: Int, _ntfKey :: OptionalInt, _ntfKeyAttempts :: OptionalInt, _ntfKeyDeleted :: OptionalInt, _ntfKeyDeleteAttempts :: OptionalInt } deriving (Show) newtype OptionalInt = OInt {toInt :: Int} deriving (Num, Show, ToJSON) newAgentSMPServerStats :: STM AgentSMPServerStats newAgentSMPServerStats = do sentDirect <- newTVar 0 sentViaProxy <- newTVar 0 sentProxied <- newTVar 0 sentDirectAttempts <- newTVar 0 sentViaProxyAttempts <- newTVar 0 sentProxiedAttempts <- newTVar 0 sentAuthErrs <- newTVar 0 sentQuotaErrs <- newTVar 0 sentExpiredErrs <- newTVar 0 sentOtherErrs <- newTVar 0 recvMsgs <- newTVar 0 recvDuplicates <- newTVar 0 recvCryptoErrs <- newTVar 0 recvErrs <- newTVar 0 ackMsgs <- newTVar 0 ackAttempts <- newTVar 0 ackNoMsgErrs <- newTVar 0 ackOtherErrs <- newTVar 0 connCreated <- newTVar 0 connSecured <- newTVar 0 connCompleted <- newTVar 0 connDeleted <- newTVar 0 connDelAttempts <- newTVar 0 connDelErrs <- newTVar 0 connSubscribed <- newTVar 0 connSubAttempts <- newTVar 0 connSubIgnored <- newTVar 0 connSubErrs <- newTVar 0 ntfKey <- newTVar 0 ntfKeyAttempts <- newTVar 0 ntfKeyDeleted <- newTVar 0 ntfKeyDeleteAttempts <- newTVar 0 pure AgentSMPServerStats { sentDirect, sentViaProxy, sentProxied, sentDirectAttempts, sentViaProxyAttempts, sentProxiedAttempts, sentAuthErrs, sentQuotaErrs, sentExpiredErrs, sentOtherErrs, recvMsgs, recvDuplicates, recvCryptoErrs, recvErrs, ackMsgs, ackAttempts, ackNoMsgErrs, ackOtherErrs, connCreated, connSecured, connCompleted, connDeleted, connDelAttempts, connDelErrs, connSubscribed, connSubAttempts, connSubIgnored, connSubErrs, ntfKey, ntfKeyAttempts, ntfKeyDeleted, ntfKeyDeleteAttempts } newAgentSMPServerStatsData :: AgentSMPServerStatsData newAgentSMPServerStatsData = AgentSMPServerStatsData { _sentDirect = 0, _sentViaProxy = 0, _sentProxied = 0, _sentDirectAttempts = 0, _sentViaProxyAttempts = 0, _sentProxiedAttempts = 0, _sentAuthErrs = 0, _sentQuotaErrs = 0, _sentExpiredErrs = 0, _sentOtherErrs = 0, _recvMsgs = 0, _recvDuplicates = 0, _recvCryptoErrs = 0, _recvErrs = 0, _ackMsgs = 0, _ackAttempts = 0, _ackNoMsgErrs = 0, _ackOtherErrs = 0, _connCreated = 0, _connSecured = 0, _connCompleted = 0, _connDeleted = 0, _connDelAttempts = 0, _connDelErrs = 0, _connSubscribed = 0, _connSubAttempts = 0, _connSubIgnored = 0, _connSubErrs = 0, _ntfKey = 0, _ntfKeyAttempts = 0, _ntfKeyDeleted = 0, _ntfKeyDeleteAttempts = 0 } newAgentSMPServerStats' :: AgentSMPServerStatsData -> STM AgentSMPServerStats newAgentSMPServerStats' s = do sentDirect <- newTVar $ _sentDirect s sentViaProxy <- newTVar $ _sentViaProxy s sentProxied <- newTVar $ _sentProxied s sentDirectAttempts <- newTVar $ _sentDirectAttempts s sentViaProxyAttempts <- newTVar $ _sentViaProxyAttempts s sentProxiedAttempts <- newTVar $ _sentProxiedAttempts s sentAuthErrs <- newTVar $ _sentAuthErrs s sentQuotaErrs <- newTVar $ _sentQuotaErrs s sentExpiredErrs <- newTVar $ _sentExpiredErrs s sentOtherErrs <- newTVar $ _sentOtherErrs s recvMsgs <- newTVar $ _recvMsgs s recvDuplicates <- newTVar $ _recvDuplicates s recvCryptoErrs <- newTVar $ _recvCryptoErrs s recvErrs <- newTVar $ _recvErrs s ackMsgs <- newTVar $ _ackMsgs s ackAttempts <- newTVar $ _ackAttempts s ackNoMsgErrs <- newTVar $ _ackNoMsgErrs s ackOtherErrs <- newTVar $ _ackOtherErrs s connCreated <- newTVar $ _connCreated s connSecured <- newTVar $ _connSecured s connCompleted <- newTVar $ _connCompleted s connDeleted <- newTVar $ _connDeleted s connDelAttempts <- newTVar $ _connDelAttempts s connDelErrs <- newTVar $ _connDelErrs s connSubscribed <- newTVar $ _connSubscribed s connSubAttempts <- newTVar $ _connSubAttempts s connSubIgnored <- newTVar $ _connSubIgnored s connSubErrs <- newTVar $ _connSubErrs s ntfKey <- newTVar $ toInt $ _ntfKey s ntfKeyAttempts <- newTVar $ toInt $ _ntfKeyAttempts s ntfKeyDeleted <- newTVar $ toInt $ _ntfKeyDeleted s ntfKeyDeleteAttempts <- newTVar $ toInt $ _ntfKeyDeleteAttempts s pure AgentSMPServerStats { sentDirect, sentViaProxy, sentProxied, sentDirectAttempts, sentViaProxyAttempts, sentProxiedAttempts, sentAuthErrs, sentQuotaErrs, sentExpiredErrs, sentOtherErrs, recvMsgs, recvDuplicates, recvCryptoErrs, recvErrs, ackMsgs, ackAttempts, ackNoMsgErrs, ackOtherErrs, connCreated, connSecured, connCompleted, connDeleted, connDelAttempts, connDelErrs, connSubscribed, connSubAttempts, connSubIgnored, connSubErrs, ntfKey, ntfKeyAttempts, ntfKeyDeleted, ntfKeyDeleteAttempts } -- as this is used to periodically update stats in db, -- this is not STM to decrease contention with stats updates getAgentSMPServerStats :: AgentSMPServerStats -> IO AgentSMPServerStatsData getAgentSMPServerStats s = do _sentDirect <- readTVarIO $ sentDirect s _sentViaProxy <- readTVarIO $ sentViaProxy s _sentProxied <- readTVarIO $ sentProxied s _sentDirectAttempts <- readTVarIO $ sentDirectAttempts s _sentViaProxyAttempts <- readTVarIO $ sentViaProxyAttempts s _sentProxiedAttempts <- readTVarIO $ sentProxiedAttempts s _sentAuthErrs <- readTVarIO $ sentAuthErrs s _sentQuotaErrs <- readTVarIO $ sentQuotaErrs s _sentExpiredErrs <- readTVarIO $ sentExpiredErrs s _sentOtherErrs <- readTVarIO $ sentOtherErrs s _recvMsgs <- readTVarIO $ recvMsgs s _recvDuplicates <- readTVarIO $ recvDuplicates s _recvCryptoErrs <- readTVarIO $ recvCryptoErrs s _recvErrs <- readTVarIO $ recvErrs s _ackMsgs <- readTVarIO $ ackMsgs s _ackAttempts <- readTVarIO $ ackAttempts s _ackNoMsgErrs <- readTVarIO $ ackNoMsgErrs s _ackOtherErrs <- readTVarIO $ ackOtherErrs s _connCreated <- readTVarIO $ connCreated s _connSecured <- readTVarIO $ connSecured s _connCompleted <- readTVarIO $ connCompleted s _connDeleted <- readTVarIO $ connDeleted s _connDelAttempts <- readTVarIO $ connDelAttempts s _connDelErrs <- readTVarIO $ connDelErrs s _connSubscribed <- readTVarIO $ connSubscribed s _connSubAttempts <- readTVarIO $ connSubAttempts s _connSubIgnored <- readTVarIO $ connSubIgnored s _connSubErrs <- readTVarIO $ connSubErrs s _ntfKey <- OInt <$> readTVarIO (ntfKey s) _ntfKeyAttempts <- OInt <$> readTVarIO (ntfKeyAttempts s) _ntfKeyDeleted <- OInt <$> readTVarIO (ntfKeyDeleted s) _ntfKeyDeleteAttempts <- OInt <$> readTVarIO (ntfKeyDeleteAttempts s) pure AgentSMPServerStatsData { _sentDirect, _sentViaProxy, _sentProxied, _sentDirectAttempts, _sentViaProxyAttempts, _sentProxiedAttempts, _sentAuthErrs, _sentQuotaErrs, _sentExpiredErrs, _sentOtherErrs, _recvMsgs, _recvDuplicates, _recvCryptoErrs, _recvErrs, _ackMsgs, _ackAttempts, _ackNoMsgErrs, _ackOtherErrs, _connCreated, _connSecured, _connCompleted, _connDeleted, _connDelAttempts, _connDelErrs, _connSubscribed, _connSubAttempts, _connSubIgnored, _connSubErrs, _ntfKey, _ntfKeyAttempts, _ntfKeyDeleted, _ntfKeyDeleteAttempts } addSMPStatsData :: AgentSMPServerStatsData -> AgentSMPServerStatsData -> AgentSMPServerStatsData addSMPStatsData sd1 sd2 = AgentSMPServerStatsData { _sentDirect = _sentDirect sd1 + _sentDirect sd2, _sentViaProxy = _sentViaProxy sd1 + _sentViaProxy sd2, _sentProxied = _sentProxied sd1 + _sentProxied sd2, _sentDirectAttempts = _sentDirectAttempts sd1 + _sentDirectAttempts sd2, _sentViaProxyAttempts = _sentViaProxyAttempts sd1 + _sentViaProxyAttempts sd2, _sentProxiedAttempts = _sentProxiedAttempts sd1 + _sentProxiedAttempts sd2, _sentAuthErrs = _sentAuthErrs sd1 + _sentAuthErrs sd2, _sentQuotaErrs = _sentQuotaErrs sd1 + _sentQuotaErrs sd2, _sentExpiredErrs = _sentExpiredErrs sd1 + _sentExpiredErrs sd2, _sentOtherErrs = _sentOtherErrs sd1 + _sentOtherErrs sd2, _recvMsgs = _recvMsgs sd1 + _recvMsgs sd2, _recvDuplicates = _recvDuplicates sd1 + _recvDuplicates sd2, _recvCryptoErrs = _recvCryptoErrs sd1 + _recvCryptoErrs sd2, _recvErrs = _recvErrs sd1 + _recvErrs sd2, _ackMsgs = _ackMsgs sd1 + _ackMsgs sd2, _ackAttempts = _ackAttempts sd1 + _ackAttempts sd2, _ackNoMsgErrs = _ackNoMsgErrs sd1 + _ackNoMsgErrs sd2, _ackOtherErrs = _ackOtherErrs sd1 + _ackOtherErrs sd2, _connCreated = _connCreated sd1 + _connCreated sd2, _connSecured = _connSecured sd1 + _connSecured sd2, _connCompleted = _connCompleted sd1 + _connCompleted sd2, _connDeleted = _connDeleted sd1 + _connDeleted sd2, _connDelAttempts = _connDelAttempts sd1 + _connDelAttempts sd2, _connDelErrs = _connDelErrs sd1 + _connDelErrs sd2, _connSubscribed = _connSubscribed sd1 + _connSubscribed sd2, _connSubAttempts = _connSubAttempts sd1 + _connSubAttempts sd2, _connSubIgnored = _connSubIgnored sd1 + _connSubIgnored sd2, _connSubErrs = _connSubErrs sd1 + _connSubErrs sd2, _ntfKey = _ntfKey sd1 + _ntfKey sd2, _ntfKeyAttempts = _ntfKeyAttempts sd1 + _ntfKeyAttempts sd2, _ntfKeyDeleted = _ntfKeyDeleted sd1 + _ntfKeyDeleted sd2, _ntfKeyDeleteAttempts = _ntfKeyDeleteAttempts sd1 + _ntfKeyDeleteAttempts sd2 } data AgentXFTPServerStats = AgentXFTPServerStats { uploads :: TVar Int, -- total replicas uploaded to server uploadsSize :: TVar Int64, -- total size of uploaded replicas in KB uploadAttempts :: TVar Int, -- upload attempts uploadErrs :: TVar Int, -- upload errors downloads :: TVar Int, -- total replicas downloaded from server downloadsSize :: TVar Int64, -- total size of downloaded replicas in KB downloadAttempts :: TVar Int, -- download attempts downloadAuthErrs :: TVar Int, -- download AUTH errors downloadErrs :: TVar Int, -- other download errors (excluding above) deletions :: TVar Int, -- total replicas deleted from server deleteAttempts :: TVar Int, -- delete attempts deleteErrs :: TVar Int -- delete errors } data AgentXFTPServerStatsData = AgentXFTPServerStatsData { _uploads :: Int, _uploadsSize :: Int64, _uploadAttempts :: Int, _uploadErrs :: Int, _downloads :: Int, _downloadsSize :: Int64, _downloadAttempts :: Int, _downloadAuthErrs :: Int, _downloadErrs :: Int, _deletions :: Int, _deleteAttempts :: Int, _deleteErrs :: Int } deriving (Show) newAgentXFTPServerStats :: STM AgentXFTPServerStats newAgentXFTPServerStats = do uploads <- newTVar 0 uploadsSize <- newTVar 0 uploadAttempts <- newTVar 0 uploadErrs <- newTVar 0 downloads <- newTVar 0 downloadsSize <- newTVar 0 downloadAttempts <- newTVar 0 downloadAuthErrs <- newTVar 0 downloadErrs <- newTVar 0 deletions <- newTVar 0 deleteAttempts <- newTVar 0 deleteErrs <- newTVar 0 pure AgentXFTPServerStats { uploads, uploadsSize, uploadAttempts, uploadErrs, downloads, downloadsSize, downloadAttempts, downloadAuthErrs, downloadErrs, deletions, deleteAttempts, deleteErrs } newAgentXFTPServerStatsData :: AgentXFTPServerStatsData newAgentXFTPServerStatsData = AgentXFTPServerStatsData { _uploads = 0, _uploadsSize = 0, _uploadAttempts = 0, _uploadErrs = 0, _downloads = 0, _downloadsSize = 0, _downloadAttempts = 0, _downloadAuthErrs = 0, _downloadErrs = 0, _deletions = 0, _deleteAttempts = 0, _deleteErrs = 0 } newAgentXFTPServerStats' :: AgentXFTPServerStatsData -> STM AgentXFTPServerStats newAgentXFTPServerStats' s = do uploads <- newTVar $ _uploads s uploadsSize <- newTVar $ _uploadsSize s uploadAttempts <- newTVar $ _uploadAttempts s uploadErrs <- newTVar $ _uploadErrs s downloads <- newTVar $ _downloads s downloadsSize <- newTVar $ _downloadsSize s downloadAttempts <- newTVar $ _downloadAttempts s downloadAuthErrs <- newTVar $ _downloadAuthErrs s downloadErrs <- newTVar $ _downloadErrs s deletions <- newTVar $ _deletions s deleteAttempts <- newTVar $ _deleteAttempts s deleteErrs <- newTVar $ _deleteErrs s pure AgentXFTPServerStats { uploads, uploadsSize, uploadAttempts, uploadErrs, downloads, downloadsSize, downloadAttempts, downloadAuthErrs, downloadErrs, deletions, deleteAttempts, deleteErrs } -- as this is used to periodically update stats in db, -- this is not STM to decrease contention with stats updates getAgentXFTPServerStats :: AgentXFTPServerStats -> IO AgentXFTPServerStatsData getAgentXFTPServerStats s = do _uploads <- readTVarIO $ uploads s _uploadsSize <- readTVarIO $ uploadsSize s _uploadAttempts <- readTVarIO $ uploadAttempts s _uploadErrs <- readTVarIO $ uploadErrs s _downloads <- readTVarIO $ downloads s _downloadsSize <- readTVarIO $ downloadsSize s _downloadAttempts <- readTVarIO $ downloadAttempts s _downloadAuthErrs <- readTVarIO $ downloadAuthErrs s _downloadErrs <- readTVarIO $ downloadErrs s _deletions <- readTVarIO $ deletions s _deleteAttempts <- readTVarIO $ deleteAttempts s _deleteErrs <- readTVarIO $ deleteErrs s pure AgentXFTPServerStatsData { _uploads, _uploadsSize, _uploadAttempts, _uploadErrs, _downloads, _downloadsSize, _downloadAttempts, _downloadAuthErrs, _downloadErrs, _deletions, _deleteAttempts, _deleteErrs } addXFTPStatsData :: AgentXFTPServerStatsData -> AgentXFTPServerStatsData -> AgentXFTPServerStatsData addXFTPStatsData sd1 sd2 = AgentXFTPServerStatsData { _uploads = _uploads sd1 + _uploads sd2, _uploadsSize = _uploadsSize sd1 + _uploadsSize sd2, _uploadAttempts = _uploadAttempts sd1 + _uploadAttempts sd2, _uploadErrs = _uploadErrs sd1 + _uploadErrs sd2, _downloads = _downloads sd1 + _downloads sd2, _downloadsSize = _downloadsSize sd1 + _downloadsSize sd2, _downloadAttempts = _downloadAttempts sd1 + _downloadAttempts sd2, _downloadAuthErrs = _downloadAuthErrs sd1 + _downloadAuthErrs sd2, _downloadErrs = _downloadErrs sd1 + _downloadErrs sd2, _deletions = _deletions sd1 + _deletions sd2, _deleteAttempts = _deleteAttempts sd1 + _deleteAttempts sd2, _deleteErrs = _deleteErrs sd1 + _deleteErrs sd2 } data AgentNtfServerStats = AgentNtfServerStats { ntfCreated :: TVar Int, ntfCreateAttempts :: TVar Int, ntfChecked :: TVar Int, ntfCheckAttempts :: TVar Int, ntfDeleted :: TVar Int, ntfDelAttempts :: TVar Int } data AgentNtfServerStatsData = AgentNtfServerStatsData { _ntfCreated :: Int, _ntfCreateAttempts :: Int, _ntfChecked :: Int, _ntfCheckAttempts :: Int, _ntfDeleted :: Int, _ntfDelAttempts :: Int } deriving (Show) newAgentNtfServerStats :: STM AgentNtfServerStats newAgentNtfServerStats = do ntfCreated <- newTVar 0 ntfCreateAttempts <- newTVar 0 ntfChecked <- newTVar 0 ntfCheckAttempts <- newTVar 0 ntfDeleted <- newTVar 0 ntfDelAttempts <- newTVar 0 pure AgentNtfServerStats { ntfCreated, ntfCreateAttempts, ntfChecked, ntfCheckAttempts, ntfDeleted, ntfDelAttempts } newAgentNtfServerStatsData :: AgentNtfServerStatsData newAgentNtfServerStatsData = AgentNtfServerStatsData { _ntfCreated = 0, _ntfCreateAttempts = 0, _ntfChecked = 0, _ntfCheckAttempts = 0, _ntfDeleted = 0, _ntfDelAttempts = 0 } newAgentNtfServerStats' :: AgentNtfServerStatsData -> STM AgentNtfServerStats newAgentNtfServerStats' s = do ntfCreated <- newTVar $ _ntfCreated s ntfCreateAttempts <- newTVar $ _ntfCreateAttempts s ntfChecked <- newTVar $ _ntfChecked s ntfCheckAttempts <- newTVar $ _ntfCheckAttempts s ntfDeleted <- newTVar $ _ntfDeleted s ntfDelAttempts <- newTVar $ _ntfDelAttempts s pure AgentNtfServerStats { ntfCreated, ntfCreateAttempts, ntfChecked, ntfCheckAttempts, ntfDeleted, ntfDelAttempts } getAgentNtfServerStats :: AgentNtfServerStats -> IO AgentNtfServerStatsData getAgentNtfServerStats s = do _ntfCreated <- readTVarIO $ ntfCreated s _ntfCreateAttempts <- readTVarIO $ ntfCreateAttempts s _ntfChecked <- readTVarIO $ ntfChecked s _ntfCheckAttempts <- readTVarIO $ ntfCheckAttempts s _ntfDeleted <- readTVarIO $ ntfDeleted s _ntfDelAttempts <- readTVarIO $ ntfDelAttempts s pure AgentNtfServerStatsData { _ntfCreated, _ntfCreateAttempts, _ntfChecked, _ntfCheckAttempts, _ntfDeleted, _ntfDelAttempts } addNtfStatsData :: AgentNtfServerStatsData -> AgentNtfServerStatsData -> AgentNtfServerStatsData addNtfStatsData sd1 sd2 = AgentNtfServerStatsData { _ntfCreated = _ntfCreated sd1 + _ntfCreated sd2, _ntfCreateAttempts = _ntfCreateAttempts sd1 + _ntfCreateAttempts sd2, _ntfChecked = _ntfChecked sd1 + _ntfChecked sd2, _ntfCheckAttempts = _ntfCheckAttempts sd1 + _ntfCheckAttempts sd2, _ntfDeleted = _ntfDeleted sd1 + _ntfDeleted sd2, _ntfDelAttempts = _ntfDelAttempts sd1 + _ntfDelAttempts sd2 } -- Type for gathering both smp and xftp stats across all users and servers, -- to then be persisted to db as a single json. data AgentPersistedServerStats = AgentPersistedServerStats { smpServersStats :: Map (UserId, SMPServer) AgentSMPServerStatsData, xftpServersStats :: Map (UserId, XFTPServer) AgentXFTPServerStatsData, ntfServersStats :: OptionalMap (UserId, NtfServer) AgentNtfServerStatsData } deriving (Show) instance FromJSON OptionalInt where parseJSON v = OInt <$> parseJSON v omittedField = Just (OInt 0) newtype OptionalMap k v = OptionalMap (Map k v) deriving (Show, ToJSON) instance (FromJSONKey k, Ord k, FromJSON v) => FromJSON (OptionalMap k v) where parseJSON v = OptionalMap <$> parseJSON v omittedField = Just (OptionalMap M.empty) $(J.deriveJSON defaultJSON ''AgentSMPServerStatsData) $(J.deriveJSON defaultJSON ''AgentXFTPServerStatsData) $(J.deriveJSON defaultJSON ''AgentNtfServerStatsData) $(J.deriveJSON defaultJSON ''AgentPersistedServerStats) instance ToField AgentPersistedServerStats where toField = toField . encodeJSON instance FromField AgentPersistedServerStats where fromField = fromTextField_ decodeJSON