module Hasura.GC
( ourIdleGC,
)
where
import Control.Concurrent.Extended qualified as C
import GHC.Stats
import Hasura.Logging
import Hasura.Prelude
import System.Mem (performMajorGC, performMinorGC)
ourIdleGC ::
Logger Hasura ->
DiffTime ->
DiffTime ->
DiffTime ->
IO void
ourIdleGC :: forall void.
Logger Hasura -> DiffTime -> DiffTime -> DiffTime -> IO void
ourIdleGC (Logger forall a (m :: * -> *).
(ToEngineLog a Hasura, MonadIO m) =>
a -> m ()
logger) DiffTime
idleInterval DiffTime
minGCInterval DiffTime
maxNoGCInterval =
IO (IO DiffTime)
forall (m :: * -> *) (n :: * -> *).
(MonadIO m, MonadIO n) =>
m (n DiffTime)
startTimer IO (IO DiffTime) -> (IO DiffTime -> IO void) -> IO void
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Word32 -> Word32 -> Bool -> IO DiffTime -> IO void
go Word32
0 Word32
0 Bool
False
where
go :: Word32 -> Word32 -> Bool -> IO DiffTime -> IO void
go Word32
gcs_prev Word32
major_gcs_prev Bool
lastIterationPerformedGC IO DiffTime
timerSinceLastMajorGC = do
DiffTime
timeSinceLastGC <- IO DiffTime
timerSinceLastMajorGC
Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (DiffTime
timeSinceLastGC DiffTime -> DiffTime -> Bool
forall a. Ord a => a -> a -> Bool
< DiffTime
minGCInterval) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
DiffTime -> IO ()
C.sleep (DiffTime
minGCInterval DiffTime -> DiffTime -> DiffTime
forall a. Num a => a -> a -> a
- DiffTime
timeSinceLastGC)
RTSStats {Word32
gcs :: Word32
gcs :: RTSStats -> Word32
gcs, Word32
major_gcs :: Word32
major_gcs :: RTSStats -> Word32
major_gcs} <- IO RTSStats
getRTSStats
let areIdle :: Bool
areIdle = Word32
gcs Word32 -> Word32 -> Bool
forall a. Eq a => a -> a -> Bool
== Word32
gcs_prev
areOverdue :: Bool
areOverdue = DiffTime
timeSinceLastGC DiffTime -> DiffTime -> Bool
forall a. Ord a => a -> a -> Bool
> DiffTime
maxNoGCInterval
if
| Word32
major_gcs Word32 -> Word32 -> Bool
forall a. Ord a => a -> a -> Bool
> Word32
major_gcs_prev -> do
IO (IO DiffTime)
forall (m :: * -> *) (n :: * -> *).
(MonadIO m, MonadIO n) =>
m (n DiffTime)
startTimer IO (IO DiffTime) -> (IO DiffTime -> IO void) -> IO void
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Word32 -> Word32 -> Bool -> IO DiffTime -> IO void
go Word32
gcs Word32
major_gcs Bool
False
| Bool
areIdle Bool -> Bool -> Bool
|| Bool
areOverdue -> do
if Bool
lastIterationPerformedGC Bool -> Bool -> Bool
&& Bool
areIdle
then do
IO ()
performMinorGC
IO (IO DiffTime)
forall (m :: * -> *) (n :: * -> *).
(MonadIO m, MonadIO n) =>
m (n DiffTime)
startTimer IO (IO DiffTime) -> (IO DiffTime -> IO void) -> IO void
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Word32 -> Word32 -> Bool -> IO DiffTime -> IO void
go (Word32
gcs Word32 -> Word32 -> Word32
forall a. Num a => a -> a -> a
+ Word32
1) Word32
major_gcs Bool
True
else do
Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool
areOverdue Bool -> Bool -> Bool
&& Bool -> Bool
not Bool
areIdle)
(IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ UnstructuredLog -> IO ()
forall a (m :: * -> *).
(ToEngineLog a Hasura, MonadIO m) =>
a -> m ()
logger
(UnstructuredLog -> IO ()) -> UnstructuredLog -> IO ()
forall a b. (a -> b) -> a -> b
$ LogLevel -> SerializableBlob -> UnstructuredLog
UnstructuredLog LogLevel
LevelWarn
(SerializableBlob -> UnstructuredLog)
-> SerializableBlob -> UnstructuredLog
forall a b. (a -> b) -> a -> b
$ SerializableBlob
"Overdue for a major GC: forcing one even though we don't appear to be idle"
IO ()
performMajorGC
IO (IO DiffTime)
forall (m :: * -> *) (n :: * -> *).
(MonadIO m, MonadIO n) =>
m (n DiffTime)
startTimer IO (IO DiffTime) -> (IO DiffTime -> IO void) -> IO void
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Word32 -> Word32 -> Bool -> IO DiffTime -> IO void
go (Word32
gcs Word32 -> Word32 -> Word32
forall a. Num a => a -> a -> a
+ Word32
1) (Word32
major_gcs Word32 -> Word32 -> Word32
forall a. Num a => a -> a -> a
+ Word32
1) Bool
True
| Bool
otherwise -> do
DiffTime -> IO ()
C.sleep DiffTime
idleInterval
Word32 -> Word32 -> Bool -> IO DiffTime -> IO void
go Word32
gcs Word32
major_gcs Bool
False IO DiffTime
timerSinceLastMajorGC