Skip to content

Commit 21fbbf9

Browse files
authored
smp server: update message counts during message expiration, increase idle interval (#1404)
* smp server: update message counts during message expiration, increase idle interval * version * fix * flip results * version
1 parent 17a0be1 commit 21fbbf9

File tree

9 files changed

+55
-39
lines changed

9 files changed

+55
-39
lines changed

simplexmq.cabal

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cabal-version: 1.12
22

33
name: simplexmq
4-
version: 6.2.0.2
4+
version: 6.2.0.3
55
synopsis: SimpleXMQ message broker
66
description: This package includes <./docs/Simplex-Messaging-Server.html server>,
77
<./docs/Simplex-Messaging-Client.html client> and

src/Simplex/Messaging/Server.hs

+23-18
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,11 @@ smpServer :: TMVar Bool -> ServerConfig -> Maybe AttachHTTP -> M ()
163163
smpServer started cfg@ServerConfig {transports, transportConfig = tCfg} attachHTTP_ = do
164164
s <- asks server
165165
pa <- asks proxyAgent
166-
msgStats <- processServerMessages
166+
msgStats_ <- processServerMessages
167167
ntfStats <- restoreServerNtfs
168-
liftIO $ printMessageStats "messages" msgStats
168+
liftIO $ mapM_ (printMessageStats "messages") msgStats_
169169
liftIO $ printMessageStats "notifications" ntfStats
170-
restoreServerStats msgStats ntfStats
170+
restoreServerStats msgStats_ ntfStats
171171
raceAny_
172172
( serverThread s "server subscribedQ" subscribedQ subscribers subClients pendingSubEvents subscriptions cancelSub
173173
: serverThread s "server ntfSubscribedQ" ntfSubscribedQ Env.notifiers ntfSubClients pendingNtfSubEvents ntfSubscriptions (\_ -> pure ())
@@ -385,12 +385,15 @@ smpServer started cfg@ServerConfig {transports, transportConfig = tCfg} attachHT
385385
threadDelay' interval
386386
old <- expireBeforeEpoch expCfg
387387
now <- systemSeconds <$> getSystemTime
388-
Sum deleted <- withActiveMsgQueues ms $ expireQueueMsgs now ms old
389-
atomicModifyIORef'_ (msgExpired stats) (+ deleted)
390-
logInfo $ "STORE: expireMessagesThread, expired " <> tshow deleted <> " messages"
388+
msgStats@MessageStats {storedMsgsCount = stored, expiredMsgsCount = expired} <-
389+
withActiveMsgQueues ms $ expireQueueMsgs now ms old
390+
atomicWriteIORef (msgCount stats) stored
391+
atomicModifyIORef'_ (msgExpired stats) (+ expired)
392+
printMessageStats "STORE: messages" msgStats
391393
where
392-
expireQueueMsgs now ms old rId q =
393-
either (const 0) Sum <$> runExceptT (idleDeleteExpiredMsgs now ms rId q old)
394+
expireQueueMsgs now ms old rId q = fmap (fromRight newMessageStats) . runExceptT $ do
395+
(expired_, stored) <- idleDeleteExpiredMsgs now ms rId q old
396+
pure MessageStats {storedMsgsCount = stored, expiredMsgsCount = fromMaybe 0 expired_, storedQueues = 1}
394397

395398
expireNtfsThread :: ServerConfig -> M ()
396399
expireNtfsThread ServerConfig {notificationExpiration = expCfg} = do
@@ -1731,26 +1734,26 @@ exportMessages tty ms f drainMsgs = do
17311734
exitFailure
17321735
encodeMessages rId = mconcat . map (\msg -> BLD.byteString (strEncode $ MLRv3 rId msg) <> BLD.char8 '\n')
17331736

1734-
processServerMessages :: M MessageStats
1737+
processServerMessages :: M (Maybe MessageStats)
17351738
processServerMessages = do
17361739
old_ <- asks (messageExpiration . config) $>>= (liftIO . fmap Just . expireBeforeEpoch)
17371740
expire <- asks $ expireMessagesOnStart . config
17381741
asks msgStore >>= liftIO . processMessages old_ expire
17391742
where
1740-
processMessages :: Maybe Int64 -> Bool -> AMsgStore -> IO MessageStats
1743+
processMessages :: Maybe Int64 -> Bool -> AMsgStore -> IO (Maybe MessageStats)
17411744
processMessages old_ expire = \case
17421745
AMS SMSMemory ms@STMMsgStore {storeConfig = STMStoreConfig {storePath}} -> case storePath of
1743-
Just f -> ifM (doesFileExist f) (importMessages False ms f old_) (pure newMessageStats)
1744-
Nothing -> pure newMessageStats
1746+
Just f -> ifM (doesFileExist f) (Just <$> importMessages False ms f old_) (pure Nothing)
1747+
Nothing -> pure Nothing
17451748
AMS SMSJournal ms
1746-
| expire -> case old_ of
1749+
| expire -> Just <$> case old_ of
17471750
Just old -> do
17481751
logInfo "expiring journal store messages..."
17491752
withAllMsgQueues False ms $ processExpireQueue old
17501753
Nothing -> do
17511754
logInfo "validating journal store messages..."
17521755
withAllMsgQueues False ms $ processValidateQueue
1753-
| otherwise -> logWarn "skipping message expiration" $> newMessageStats
1756+
| otherwise -> logWarn "skipping message expiration" $> Nothing
17541757
where
17551758
processExpireQueue old rId q =
17561759
runExceptT expireQueue >>= \case
@@ -1887,8 +1890,8 @@ saveServerStats =
18871890
B.writeFile f $ strEncode stats
18881891
logInfo "server stats saved"
18891892

1890-
restoreServerStats :: MessageStats -> MessageStats -> M ()
1891-
restoreServerStats msgStats ntfStats = asks (serverStatsBackupFile . config) >>= mapM_ restoreStats
1893+
restoreServerStats :: Maybe MessageStats -> MessageStats -> M ()
1894+
restoreServerStats msgStats_ ntfStats = asks (serverStatsBackupFile . config) >>= mapM_ restoreStats
18921895
where
18931896
restoreStats f = whenM (doesFileExist f) $ do
18941897
logInfo $ "restoring server stats from file " <> T.pack f
@@ -1897,9 +1900,11 @@ restoreServerStats msgStats ntfStats = asks (serverStatsBackupFile . config) >>=
18971900
s <- asks serverStats
18981901
AMS _ st <- asks msgStore
18991902
_qCount <- M.size <$> readTVarIO (activeMsgQueues st)
1900-
let _msgCount = storedMsgsCount msgStats
1903+
let _msgCount = maybe statsMsgCount storedMsgsCount msgStats_
19011904
_ntfCount = storedMsgsCount ntfStats
1902-
liftIO $ setServerStats s d {_qCount, _msgCount, _ntfCount, _msgExpired = _msgExpired d + expiredMsgsCount msgStats, _msgNtfExpired = _msgNtfExpired d + expiredMsgsCount ntfStats}
1905+
_msgExpired' = _msgExpired d + maybe 0 expiredMsgsCount msgStats_
1906+
_msgNtfExpired' = _msgNtfExpired d + expiredMsgsCount ntfStats
1907+
liftIO $ setServerStats s d {_qCount, _msgCount, _ntfCount, _msgExpired = _msgExpired', _msgNtfExpired = _msgNtfExpired'}
19031908
renameFile f $ f <> ".bak"
19041909
logInfo "server stats restored"
19051910
compareCounts "Queue" statsQCount _qCount

src/Simplex/Messaging/Server/Env/STM.hs

+8-4
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ data ServerConfig = ServerConfig
8080
-- | time after which the messages can be removed from the queues and check interval, seconds
8181
messageExpiration :: Maybe ExpirationConfig,
8282
expireMessagesOnStart :: Bool,
83+
-- | interval of inactivity after which journal queue is closed
84+
idleQueueInterval :: Int64,
8385
-- | notification expiration interval (seconds)
8486
notificationExpiration :: ExpirationConfig,
8587
-- | time after which the socket with inactive client can be disconnected (without any messages or commands, incl. PING),
@@ -121,9 +123,12 @@ defaultMessageExpiration :: ExpirationConfig
121123
defaultMessageExpiration =
122124
ExpirationConfig
123125
{ ttl = defMsgExpirationDays * 86400, -- seconds
124-
checkInterval = 21600 -- seconds, 6 hours
126+
checkInterval = 14400 -- seconds, 4 hours
125127
}
126128

129+
defaultIdleQueueInterval :: Int64
130+
defaultIdleQueueInterval = 28800 -- seconds, 8 hours
131+
127132
defNtfExpirationHours :: Int64
128133
defNtfExpirationHours = 24
129134

@@ -283,15 +288,14 @@ newProhibitedSub = do
283288
return Sub {subThread = ProhibitSub, delivered}
284289

285290
newEnv :: ServerConfig -> IO Env
286-
newEnv config@ServerConfig {smpCredentials, httpCredentials, storeLogFile, msgStoreType, storeMsgsFile, smpAgentCfg, information, messageExpiration, msgQueueQuota, maxJournalMsgCount, maxJournalStateLines} = do
291+
newEnv config@ServerConfig {smpCredentials, httpCredentials, storeLogFile, msgStoreType, storeMsgsFile, smpAgentCfg, information, messageExpiration, idleQueueInterval, msgQueueQuota, maxJournalMsgCount, maxJournalStateLines} = do
287292
serverActive <- newTVarIO True
288293
server <- newServer
289294
msgStore@(AMS _ store) <- case msgStoreType of
290295
AMSType SMSMemory -> AMS SMSMemory <$> newMsgStore STMStoreConfig {storePath = storeMsgsFile, quota = msgQueueQuota}
291296
AMSType SMSJournal -> case storeMsgsFile of
292297
Just storePath ->
293-
let idleInterval = maybe maxBound checkInterval messageExpiration
294-
cfg = JournalStoreConfig {storePath, quota = msgQueueQuota, pathParts = journalMsgStoreDepth, maxMsgCount = maxJournalMsgCount, maxStateLines = maxJournalStateLines, stateTailSize = defaultStateTailSize, idleInterval}
298+
let cfg = JournalStoreConfig {storePath, quota = msgQueueQuota, pathParts = journalMsgStoreDepth, maxMsgCount = maxJournalMsgCount, maxStateLines = maxJournalStateLines, stateTailSize = defaultStateTailSize, idleInterval = idleQueueInterval}
295299
in AMS SMSJournal <$> newMsgStore cfg
296300
Nothing -> putStrLn "Error: journal msg store require path in [STORE_LOG], restore_messages" >> exitFailure
297301
ntfStore <- NtfStore <$> TM.emptyIO

src/Simplex/Messaging/Server/Main.hs

+1
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ smpServerCLI_ generateSite serveStaticFiles attachStaticFiles cfgPath logPath =
416416
{ ttl = 86400 * readIniDefault defMsgExpirationDays "STORE_LOG" "expire_messages_days" ini
417417
},
418418
expireMessagesOnStart = fromMaybe True $ iniOnOff "STORE_LOG" "expire_messages_on_start" ini,
419+
idleQueueInterval = defaultIdleQueueInterval,
419420
notificationExpiration =
420421
defaultNtfExpiration
421422
{ ttl = 3600 * readIniDefault defNtfExpirationHours "STORE_LOG" "expire_ntfs_hours" ini

src/Simplex/Messaging/Server/MsgStore/Journal.hs

+8-7
Original file line numberDiff line numberDiff line change
@@ -332,20 +332,21 @@ instance MsgStoreClass JournalMsgStore where
332332
journalId <- newJournalId random
333333
mkJournalQueue queue (newMsgQueueState journalId) Nothing
334334

335-
withIdleMsgQueue :: Int64 -> JournalMsgStore -> RecipientId -> JournalQueue -> (JournalMsgQueue -> StoreIO a) -> StoreIO (Maybe a)
335+
withIdleMsgQueue :: Int64 -> JournalMsgStore -> RecipientId -> JournalQueue -> (JournalMsgQueue -> StoreIO a) -> StoreIO (Maybe a, Int)
336336
withIdleMsgQueue now ms@JournalMsgStore {config} rId q action =
337337
StoreIO $ readTVarIO (msgQueue_ q) >>= \case
338338
Nothing ->
339-
Just <$>
340-
E.bracket
341-
(unStoreIO $ getMsgQueue ms rId q)
342-
(\_ -> closeMsgQueue q)
343-
(unStoreIO . action)
339+
E.bracket (unStoreIO $ getMsgQueue ms rId q) (\_ -> closeMsgQueue q) $ \mq -> unStoreIO $ do
340+
r <- action mq
341+
sz <- getQueueSize_ mq
342+
pure (Just r, sz)
344343
Just mq -> do
345344
ts <- readTVarIO $ activeAt q
346-
if now - ts >= idleInterval config
345+
r <- if now - ts >= idleInterval config
347346
then Just <$> unStoreIO (action mq) `E.finally` closeMsgQueue q
348347
else pure Nothing
348+
sz <- unStoreIO $ getQueueSize_ mq
349+
pure (r, sz)
349350

350351
deleteQueue :: JournalMsgStore -> RecipientId -> JournalQueue -> IO (Either ErrorType QueueRec)
351352
deleteQueue ms rId q =

src/Simplex/Messaging/Server/MsgStore/STM.hs

+7-3
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,13 @@ instance MsgStoreClass STMMsgStore where
110110
pure q
111111

112112
-- does not create queue if it does not exist, does not delete it if it does (can't just close in-memory queue)
113-
withIdleMsgQueue :: Int64 -> STMMsgStore -> RecipientId -> STMQueue -> (STMMsgQueue -> STM a) -> STM (Maybe a)
114-
withIdleMsgQueue _ _ _ STMQueue {msgQueue_} action = readTVar msgQueue_ >>= mapM action
115-
{-# INLINE withIdleMsgQueue #-}
113+
withIdleMsgQueue :: Int64 -> STMMsgStore -> RecipientId -> STMQueue -> (STMMsgQueue -> STM a) -> STM (Maybe a, Int)
114+
withIdleMsgQueue _ _ _ STMQueue {msgQueue_} action = readTVar msgQueue_ >>= \case
115+
Just q -> do
116+
r <- action q
117+
sz <- getQueueSize_ q
118+
pure (Just r, sz)
119+
Nothing -> pure (Nothing, 0)
116120

117121
deleteQueue :: STMMsgStore -> RecipientId -> STMQueue -> IO (Either ErrorType QueueRec)
118122
deleteQueue ms rId q = fst <$$> deleteQueue' ms rId q

src/Simplex/Messaging/Server/MsgStore/Types.hs

+5-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import Control.Monad (foldM)
1515
import Control.Monad.Trans.Except
1616
import Data.Int (Int64)
1717
import Data.Kind
18-
import Data.Maybe (fromMaybe)
1918
import qualified Data.Map.Strict as M
2019
import Data.Time.Clock.System (SystemTime (systemSeconds))
2120
import Simplex.Messaging.Protocol
@@ -47,7 +46,7 @@ class Monad (StoreMonad s) => MsgStoreClass s where
4746
queueRec' :: StoreQueue s -> TVar (Maybe QueueRec)
4847
getMsgQueue :: s -> RecipientId -> StoreQueue s -> StoreMonad s (MsgQueue s)
4948
-- the journal queue will be closed after action if it was initially closed or idle longer than interval in config
50-
withIdleMsgQueue :: Int64 -> s -> RecipientId -> StoreQueue s -> (MsgQueue s -> StoreMonad s a) -> StoreMonad s (Maybe a)
49+
withIdleMsgQueue :: Int64 -> s -> RecipientId -> StoreQueue s -> (MsgQueue s -> StoreMonad s a) -> StoreMonad s (Maybe a, Int)
5150
deleteQueue :: s -> RecipientId -> StoreQueue s -> IO (Either ErrorType QueueRec)
5251
deleteQueueSize :: s -> RecipientId -> StoreQueue s -> IO (Either ErrorType (QueueRec, Int))
5352
getQueueMessages_ :: Bool -> MsgQueue s -> StoreMonad s [Message]
@@ -114,10 +113,11 @@ deleteExpiredMsgs st rId q old =
114113
getMsgQueue st rId q >>= deleteExpireMsgs_ old q
115114

116115
-- closed and idle queues will be closed after expiration
117-
idleDeleteExpiredMsgs :: MsgStoreClass s => Int64 -> s -> RecipientId -> StoreQueue s -> Int64 -> ExceptT ErrorType IO Int
116+
-- returns (expired count, queue size after expiration)
117+
idleDeleteExpiredMsgs :: MsgStoreClass s => Int64 -> s -> RecipientId -> StoreQueue s -> Int64 -> ExceptT ErrorType IO (Maybe Int, Int)
118118
idleDeleteExpiredMsgs now st rId q old =
119-
isolateQueue rId q "idleDeleteExpiredMsgs" $
120-
fromMaybe 0 <$> withIdleMsgQueue now st rId q (deleteExpireMsgs_ old q)
119+
isolateQueue rId q "idleDeleteExpiredMsgs" $
120+
withIdleMsgQueue now st rId q (deleteExpireMsgs_ old q)
121121

122122
deleteExpireMsgs_ :: MsgStoreClass s => Int64 -> StoreQueue s -> MsgQueue s -> StoreMonad s Int
123123
deleteExpireMsgs_ old q mq = do

tests/SMPClient.hs

+1
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ cfgMS msType =
133133
controlPortAdminAuth = Nothing,
134134
messageExpiration = Just defaultMessageExpiration,
135135
expireMessagesOnStart = True,
136+
idleQueueInterval = defaultIdleQueueInterval,
136137
notificationExpiration = defaultNtfExpiration,
137138
inactiveClientExpiration = Just defaultInactiveClientExpiration,
138139
logStatsInterval = Nothing,

tests/ServerTests.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -972,7 +972,7 @@ testMsgExpireOnInterval =
972972
xit' "should expire messages that are not received before messageTTL after expiry interval" $ \(ATransport (t :: TProxy c), msType) -> do
973973
g <- C.newRandom
974974
(sPub, sKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g
975-
let cfg' = (cfgMS msType) {messageExpiration = Just ExpirationConfig {ttl = 1, checkInterval = 1}}
975+
let cfg' = (cfgMS msType) {messageExpiration = Just ExpirationConfig {ttl = 1, checkInterval = 1}, idleQueueInterval = 1}
976976
withSmpServerConfigOn (ATransport t) cfg' testPort $ \_ ->
977977
testSMPClient @c $ \sh -> do
978978
(sId, rId, rKey, _) <- testSMPClient @c $ \rh -> createAndSecureQueue rh sPub

0 commit comments

Comments
 (0)