@@ -53,6 +53,7 @@ import Data.List (intercalate)
5353import Data.Maybe (catMaybes , fromMaybe )
5454import qualified Data.Text as T
5555import Data.Time.Clock (getCurrentTime )
56+ import Data.Time.Clock.System (SystemTime (.. ), getSystemTime )
5657import Data.Time.Format.ISO8601 (iso8601Show )
5758import GHC.IO (catchAny )
5859import Simplex.Messaging.Agent.Client (getMapLock , withLockMap )
@@ -92,7 +93,9 @@ data JournalStoreConfig = JournalStoreConfig
9293 -- This number should be set bigger than queue quota.
9394 maxMsgCount :: Int ,
9495 maxStateLines :: Int ,
95- stateTailSize :: Int
96+ stateTailSize :: Int ,
97+ -- time in seconds after which the queue will be closed after message expiration
98+ idleInterval :: Int64
9699 }
97100
98101data JournalQueue = JournalQueue
@@ -116,7 +119,9 @@ data JournalMsgQueue = JournalMsgQueue
116119 -- It prevents reading each message twice,
117120 -- and reading it after it was just written.
118121 tipMsg :: TVar (Maybe (Maybe (Message , Int64 ))),
119- handles :: TVar (Maybe MsgQueueHandles )
122+ handles :: TVar (Maybe MsgQueueHandles ),
123+ -- system time in seconds since epoch
124+ activeAt :: TVar Int64
120125 }
121126
122127data MsgQueueState = MsgQueueState
@@ -295,11 +300,11 @@ instance MsgStoreClass JournalMsgStore where
295300 (Nothing <$ putStrLn (" Error: path " <> path' <> " is not a directory, skipping" ))
296301
297302 logQueueStates :: JournalMsgStore -> IO ()
298- logQueueStates ms = withActiveMsgQueues ms $ \ _ -> logQueueState
303+ logQueueStates ms = withActiveMsgQueues ms $ \ _ -> unStoreIO . logQueueState
299304
300- logQueueState :: JournalQueue -> IO ()
305+ logQueueState :: JournalQueue -> StoreIO ()
301306 logQueueState q =
302- void $
307+ StoreIO . void $
303308 readTVarIO (msgQueue_ q)
304309 $>>= \ mq -> readTVarIO (handles mq)
305310 $>>= (\ hs -> (readTVarIO (state mq) >>= appendState (stateHandle hs)) $> Just () )
@@ -326,9 +331,21 @@ instance MsgStoreClass JournalMsgStore where
326331 journalId <- newJournalId random
327332 mkJournalQueue queue (newMsgQueueState journalId) Nothing
328333
329- openedMsgQueue :: JournalQueue -> StoreIO (Maybe JournalMsgQueue )
330- openedMsgQueue = StoreIO . readTVarIO . msgQueue_
331- {-# INLINE openedMsgQueue #-}
334+ withIdleMsgQueue :: Int64 -> JournalMsgStore -> RecipientId -> JournalQueue -> (JournalMsgQueue -> StoreIO a ) -> StoreIO (Maybe a )
335+ withIdleMsgQueue now ms@ JournalMsgStore {config} rId q action =
336+ StoreIO $ readTVarIO (msgQueue_ q) >>= maybe runQ idleQ
337+ where
338+ runQ =
339+ Just <$>
340+ E. bracket
341+ (unStoreIO $ getMsgQueue ms rId q)
342+ (\ _ -> closeMsgQueue q)
343+ (unStoreIO . action)
344+ idleQ mq = do
345+ ts <- readTVarIO $ activeAt mq
346+ if now - ts > idleInterval config
347+ then Just <$> unStoreIO (action mq) `E.finally` closeMsgQueue q
348+ else pure Nothing
332349
333350 deleteQueue :: JournalMsgStore -> RecipientId -> JournalQueue -> IO (Either ErrorType QueueRec )
334351 deleteQueue ms rId q =
@@ -355,7 +372,7 @@ instance MsgStoreClass JournalMsgStore where
355372 writeMsg :: JournalMsgStore -> RecipientId -> JournalQueue -> Bool -> Message -> ExceptT ErrorType IO (Maybe (Message , Bool ))
356373 writeMsg ms rId q' logState msg = isolateQueue rId q' " writeMsg" $ do
357374 q <- getMsgQueue ms rId q'
358- StoreIO $ do
375+ StoreIO $ ( `E.finally` updateActiveAt q) $ do
359376 st@ MsgQueueState {canWrite, size} <- readTVarIO (state q)
360377 let empty = size == 0
361378 if canWrite || empty
@@ -420,7 +437,7 @@ instance MsgStoreClass JournalMsgStore where
420437 pure $ Just msg
421438
422439 tryDeleteMsg_ :: JournalMsgQueue -> Bool -> StoreIO ()
423- tryDeleteMsg_ q@ JournalMsgQueue {tipMsg, handles} logState = StoreIO $
440+ tryDeleteMsg_ q@ JournalMsgQueue {tipMsg, handles} logState = StoreIO $ ( `E.finally` updateActiveAt q) $
424441 void $
425442 readTVarIO tipMsg -- if there is no cached tipMsg, do nothing
426443 $>>= (pure . fmap snd )
@@ -431,6 +448,9 @@ instance MsgStoreClass JournalMsgStore where
431448 isolateQueue rId JournalQueue {queueLock} op =
432449 tryStore' op rId . withLock' queueLock op . unStoreIO
433450
451+ updateActiveAt :: JournalMsgQueue -> IO ()
452+ updateActiveAt q = atomically . writeTVar (activeAt q) . systemSeconds =<< getSystemTime
453+
434454tryStore' :: String -> RecipientId -> IO a -> ExceptT ErrorType IO a
435455tryStore' op rId = tryStore op rId . fmap Right
436456
@@ -457,9 +477,10 @@ mkJournalQueue queue st hs_ = do
457477 state <- newTVarIO st
458478 tipMsg <- newTVarIO Nothing
459479 handles <- newTVarIO hs_
480+ activeAt <- newTVarIO . systemSeconds =<< getSystemTime
460481 -- using the same queue lock which is currently locked,
461482 -- to avoid map lookup on queue operations
462- pure JournalMsgQueue {queue, state, tipMsg, handles}
483+ pure JournalMsgQueue {queue, state, tipMsg, handles, activeAt }
463484
464485chooseReadJournal :: JournalMsgQueue -> Bool -> MsgQueueHandles -> IO (Maybe (JournalState 'JTRead, Handle ))
465486chooseReadJournal q log' hs = do
0 commit comments