Compare commits

...
3 Commits
Author SHA1 Message Date
Evgeny Poberezkin 27a37387be 7.0.1.0 2026-07-31 15:20:41 +01:00
sh d65d790a20 ntf server: shard push workers per notification token (#1840)
* ntf server: shard push workers per notification token

* ntf server: inline push worker shard calculation
2026-07-31 15:19:43 +01:00
shandEvgeny Poberezkin 7d0820dd44 smp server: do not create messaging queues in SMP proxy to prevent deadlock in processing (#1839)
* smp: fix proxy message queue memory leak

* smp server: do not create messaging queues in SMP proxy to prevent deadlock in processing

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2026-07-30 16:33:27 +01:00
7 changed files with 20 additions and 16 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
cabal-version: 3.0
name: simplexmq
version: 7.0.0.6
version: 7.0.1.0
synopsis: SimpleXMQ message broker
description: This package includes <./docs/Simplex-Messaging-Server.html server>,
<./docs/Simplex-Messaging-Client.html client> and
@@ -50,7 +50,6 @@ import Data.Bits (xor)
import Data.ByteArray (ScrubbedBytes)
import qualified Data.ByteArray as BA
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Data.Functor (($>))
import Data.IORef
import Data.Maybe (fromMaybe)
+6 -5
View File
@@ -107,7 +107,7 @@ data SMPClientAgentConfig = SMPClientAgentConfig
{ smpCfg :: ProtocolClientConfig SMPVersion,
reconnectInterval :: RetryInterval,
persistErrorInterval :: NominalDiffTime,
msgQSize :: Natural,
msgQSize :: Maybe Natural,
agentQSize :: Natural,
agentSubsBatchSize :: Int,
ownServerDomains :: [ByteString]
@@ -124,7 +124,7 @@ defaultSMPClientAgentConfig =
maxInterval = 10 * second
},
persistErrorInterval = 30, -- seconds
msgQSize = 2048,
msgQSize = Just 2048,
agentQSize = 2048,
agentSubsBatchSize = 1360,
ownServerDomains = []
@@ -138,7 +138,7 @@ data SMPClientAgent p = SMPClientAgent
dbService :: Maybe DBService,
active :: TVar Bool,
startedAt :: UTCTime,
msgQ :: TBQueue (ServerTransmissionBatch SMPVersion ErrorType BrokerMsg),
msgQ :: Maybe (TBQueue (ServerTransmissionBatch SMPVersion ErrorType BrokerMsg)),
agentQ :: TBQueue SMPClientAgentEvent,
randomDrg :: TVar ChaChaDRG,
smpClients :: TMap SMPServer SMPClientVar,
@@ -162,7 +162,8 @@ newSMPClientAgent :: SParty p -> SMPClientAgentConfig -> Maybe DBService -> TVar
newSMPClientAgent agentParty agentCfg@SMPClientAgentConfig {msgQSize, agentQSize} dbService randomDrg = do
active <- newTVarIO True
startedAt <- getCurrentTime
msgQ <- newTBQueueIO msgQSize
-- Only subscribing agents receive server transmissions, should not be created until processed to prevent deadlock.
msgQ <- mapM newTBQueueIO msgQSize
agentQ <- newTBQueueIO agentQSize
smpClients <- TM.emptyIO
smpSessions <- TM.emptyIO
@@ -264,7 +265,7 @@ connectClient ca@SMPClientAgent {agentCfg, dbService, smpClients, smpSessions, m
Nothing -> getClient cfg
where
cfg = smpCfg agentCfg
getClient cfg' = getProtocolClient randomDrg NRMBackground (1, srv, Nothing) cfg' [] (Just msgQ) startedAt clientDisconnected
getClient cfg' = getProtocolClient randomDrg NRMBackground (1, srv, Nothing) cfg' [] msgQ startedAt clientDisconnected
clientDisconnected :: SMPClient -> IO ()
clientDisconnected smp = do
+10 -7
View File
@@ -34,6 +34,7 @@ import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as B
import Data.Either (partitionEithers)
import Data.Functor (($>))
import Data.Hashable (hash)
import Data.IORef
import Data.Int (Int64)
import qualified Data.IntSet as IS
@@ -526,10 +527,10 @@ subscribeNtfs NtfSubscriber {smpSubscribers, subscriberSeq, smpAgent = ca} st sm
subscribeQueuesNtfs ca smpServer' [sub]
ntfSubscriber :: NtfSubscriber -> M ()
ntfSubscriber NtfSubscriber {smpAgent = ca@SMPClientAgent {msgQ, agentQ}} =
ntfSubscriber NtfSubscriber {smpAgent = ca@SMPClientAgent {msgQ = msgQ_, agentQ}} =
race_ receiveSMP receiveAgent
where
receiveSMP = do
receiveSMP = forM_ msgQ_ $ \msgQ -> do
st <- asks store
ps <- asks pushServer
stats <- asks serverStats
@@ -640,11 +641,13 @@ showServer' :: SMPServer -> Text
showServer' = decodeLatin1 . strEncode . host
pushNotification :: NtfPushServer -> Maybe T.Text -> OwnServer -> NtfTknRec -> PushNotification -> M ()
pushNotification s srvHost_ isOwn tkn@NtfTknRec {token = token@(DeviceToken pp _)} ntf =
pushNotification s srvHost_ isOwn tkn@NtfTknRec {ntfTknId, token = token@(DeviceToken pp _)} ntf =
ifM
(pushProviderAllowed token)
(getOrCreatePushWorker s (srvHost_, pp) isOwn >>= atomically . (`writeTBQueue` (tkn, ntf)))
(getOrCreatePushWorker s (srvHost_, pp, hash (unEntityId ntfTknId) `mod` pushWorkersPerServer) isOwn >>= atomically . (`writeTBQueue` (tkn, ntf)))
(logWarn "skipping disabled APNS test push provider")
where
pushWorkersPerServer = 8
pushProviderAllowed :: DeviceToken -> M Bool
pushProviderAllowed (DeviceToken PPApnsTest _) = asks (allowTestPushProvider . config)
@@ -657,8 +660,8 @@ guardPushProvider token action =
action
(pure $ NRErr $ CMD SMP.PROHIBITED)
getOrCreatePushWorker :: NtfPushServer -> (Maybe T.Text, PushProvider) -> OwnServer -> M (TBQueue (NtfTknRec, PushNotification))
getOrCreatePushWorker s@NtfPushServer {pushWorkers, pushWorkerSeq, pushQSize} key@(srvHost_, _) isOwn = do
getOrCreatePushWorker :: NtfPushServer -> (Maybe T.Text, PushProvider, Int) -> OwnServer -> M (TBQueue (NtfTknRec, PushNotification))
getOrCreatePushWorker s@NtfPushServer {pushWorkers, pushWorkerSeq, pushQSize} key@(srvHost_, _, _) isOwn = do
ts <- liftIO getCurrentTime
withGetSessVar' pushWorkerSeq key pushWorkers ts createWorker existingWorker
where
@@ -731,7 +734,7 @@ runPushWorker s srvHost_ isOwn q = forever $ do
_ -> err e
err e = logError ("Push provider error (" <> tshow pp <> ", " <> tshow ntfTknId <> "): " <> tshow e) $> Left e
pushWorkersQLength :: TMap (Maybe T.Text, PushProvider) PushWorkerVar -> IO Natural
pushWorkersQLength :: TMap (Maybe T.Text, PushProvider, Int) PushWorkerVar -> IO Natural
pushWorkersQLength workers = do
ws <- readTVarIO workers
foldM addQLength 0 ws
@@ -174,7 +174,7 @@ data SMPSubscriber = SMPSubscriber
}
data NtfPushServer = NtfPushServer
{ pushWorkers :: TMap (Maybe T.Text, PushProvider) PushWorkerVar,
{ pushWorkers :: TMap (Maybe T.Text, PushProvider, Int) PushWorkerVar, -- Int is the worker shard
pushWorkerSeq :: TVar Int,
pushQSize :: Natural,
pushClients :: TMap PushProvider PushClientVar,
+1
View File
@@ -604,6 +604,7 @@ smpServerCLI_ generateSite serveStaticFiles attachStaticFiles cfgPath logPath =
}
},
ownServerDomains = either (const []) textToOwnServers $ lookupValue "PROXY" "own_server_domains" ini,
msgQSize = Nothing, -- to prevent accumulation of late responses, and deadlocks in SMP proxy
persistErrorInterval = 30 -- seconds
},
allowSMPProxy = True,
+1 -1
View File
@@ -275,7 +275,7 @@ cfgMS msType = withStoreCfg (testServerStoreConfig msType) $ \serverStoreCfg ->
smpServerVRange = supportedServerSMPRelayVRange,
transportConfig = mkTransportServerConfig True (Just alpnSupportedSMPHandshakes) True,
controlPort = Nothing,
smpAgentCfg = defaultSMPClientAgentConfig {persistErrorInterval = 1}, -- seconds
smpAgentCfg = defaultSMPClientAgentConfig {persistErrorInterval = 1, msgQSize = Nothing}, -- seconds
allowSMPProxy = False,
serverClientConcurrency = 2,
serverResolverConcurrency = defaultNameResolverConcurrency,