smp-server: fix notification store key leak (#1907)

* smp-server: remove expired notification store keys

* smp-server: remove delivered notification keys

* tests: generate MsgId for ntf store test

* smp-server: keep ntf store comments in place

* smp-server: strict count of expired notifications
This commit is contained in:
sh authored and GitHub committed 2026-10-23 09:55:12 +01:00
1 parent f795c44b64
commit 8286303d0f
3 files changed
+79 -20

No files matched your search

+2 -1
View File
@@ -366,7 +366,7 @@ smpServer started cfg@ServerConfig {transports, transportConfig = tCfg, startOpt
runDeliverNtfs ms ns' stats
where
runDeliverNtfs :: s -> NtfStore -> ServerStats -> IO ()
runDeliverNtfs ms (NtfStore ns) stats = do
runDeliverNtfs ms ns'@(NtfStore ns) stats = do
ntfs <- M.assocs <$> readTVarIO ns
unless (null ntfs) $
getQueueNtfServices @(StoreQueue s) (queueStore ms) ntfs >>= \case
@@ -378,6 +378,7 @@ smpServer started cfg@ServerConfig {transports, transportConfig = tCfg, startOpt
cIds <- IS.toList <$> readTVarIO subClients
forM_ cIds $ \cId -> getServerClient cId srv >>= mapM_ (deliverQueueNtfs ntfs')
atomically $ modifyTVar' ns (`M.withoutKeys` S.fromList (map fst deleted))
deleteEmptyNtfs ns' ntfs
where
deliverQueueNtfs ntfs' c@Client {ntfSubscriptions} =
whenM (currentClient readTVarIO c) $ do
+22 -16
View File
@@ -10,10 +10,12 @@ module Simplex.Messaging.Server.NtfStore
storeNtf,
deleteNtfs,
deleteExpiredNtfs,
deleteEmptyNtfs,
) where
import Control.Concurrent.STM
import Control.Monad (foldM)
import Data.Functor (($>))
import Data.Int (Int64)
import qualified Data.Map.Strict as M
import Data.Time.Clock.System (SystemTime (..))
@@ -22,6 +24,7 @@ import Simplex.Messaging.Encoding.String
import Simplex.Messaging.Protocol (EncNMsgMeta, MsgId, NotifierId)
import Simplex.Messaging.TMap (TMap)
import qualified Simplex.Messaging.TMap as TM
import Simplex.Messaging.Util (whenM)
newtype NtfStore = NtfStore (TMap NotifierId (TVar [MsgNtf]))
@@ -33,13 +36,11 @@ data MsgNtf = MsgNtf
}
storeNtf :: NtfStore -> NotifierId -> MsgNtf -> IO ()
storeNtf (NtfStore ns) nId ntf = do
TM.lookupIO nId ns >>= atomically . maybe newNtfs (`modifyTVar'` (ntf :))
storeNtf (NtfStore ns) nId ntf =
atomically $ TM.lookup nId ns >>= maybe (TM.insertM nId (newTVar [ntf]) ns) (`modifyTVar'` (ntf :))
-- TODO [ntfdb] coalesce messages here once the client is updated to process multiple messages
-- for single notification.
-- when (isJust prevNtf) $ incStat $ msgNtfReplaced stats
where
newNtfs = TM.lookup nId ns >>= maybe (TM.insertM nId (newTVar [ntf]) ns) (`modifyTVar'` (ntf :))
deleteNtfs :: NtfStore -> NotifierId -> IO Int
deleteNtfs (NtfStore ns) nId = atomically (TM.lookupDelete nId ns) >>= maybe (pure 0) (fmap length . readTVarIO)
@@ -48,18 +49,23 @@ deleteExpiredNtfs :: NtfStore -> Int64 -> IO Int
deleteExpiredNtfs (NtfStore ns) old =
foldM (\expired -> fmap (expired +) . expireQueue) 0 . M.keys =<< readTVarIO ns
where
expireQueue nId = TM.lookupIO nId ns >>= maybe (pure 0) expire
expire v = readTVarIO v >>= \case
[] -> pure 0
_ ->
atomically $ readTVar v >>= \case
[] -> pure 0
-- check the last message first, it is the earliest
ntfs | systemSeconds (ntfTs $ last $ ntfs) < old -> do
let !ntfs' = filter (\MsgNtf {ntfTs = ts} -> systemSeconds ts >= old) ntfs
writeTVar v ntfs'
pure $! length ntfs - length ntfs'
_ -> pure 0
expireQueue nId = atomically $ TM.lookup nId ns >>= maybe (pure 0) (expire nId)
expire nId v = readTVar v >>= \case
[] -> TM.delete nId ns $> 0
-- check the last message first, it is the earliest
ntfs | systemSeconds (ntfTs $ last $ ntfs) < old -> do
let !ntfs' = filter (\MsgNtf {ntfTs = ts} -> systemSeconds ts >= old) ntfs
if null ntfs' then TM.delete nId ns else writeTVar v ntfs'
pure $! length ntfs - length ntfs'
_ -> pure 0
deleteEmptyNtfs :: NtfStore -> [(NotifierId, TVar [MsgNtf])] -> IO ()
deleteEmptyNtfs (NtfStore ns) = mapM_ deleteEmpty
where
deleteEmpty (nId, v) =
whenM (null <$> readTVarIO v) $
atomically $
TM.lookup nId ns >>= mapM_ (\v' -> whenM (null <$> readTVar v') $ TM.delete nId ns)
data NtfLogRecord = NLRv1 NotifierId MsgNtf