Projects STRLCPY graphql-engine Commits 956a698f
🤬
  • server: use lighter GC in ourIdleGC when idle

    Previously when users had an idle system with a large working set (i.e. large schema) they would likely see CPU spikes every 10 seconds.
    
    See: https://github.com/hasura/graphql-engine/issues/9592#issuecomment-1580543694
    
    Now we perform a lighter-weight minor GC in that case.
    
    PR-URL: https://github.com/hasura/graphql-engine-mono/pull/9841
    GitOrigin-RevId: 2a49a83c4b763546a901558641ab9b6460ebffd9
  • Loading...
  • Brandon Simmons committed with hasura-bot 2 months ago
    956a698f
    1 parent d966e0a9
  • ■ ■ ■ ■ ■ ■
    server/src-lib/Hasura/GC.hs
    skipped 6 lines
    7 7  import GHC.Stats
    8 8  import Hasura.Logging
    9 9  import Hasura.Prelude
    10  -import System.Mem (performMajorGC)
     10 +import System.Mem (performMajorGC, performMinorGC)
    11 11   
    12 12  -- | The RTS's idle GC doesn't work for us:
    13 13  --
    skipped 20 lines
    34 34   DiffTime ->
    35 35   IO void
    36 36  ourIdleGC (Logger logger) idleInterval minGCInterval maxNoGCInterval =
    37  - startTimer >>= go 0 0
     37 + startTimer >>= go 0 0 False
    38 38   where
    39  - go gcs_prev major_gcs_prev timerSinceLastMajorGC = do
     39 + go gcs_prev major_gcs_prev lastIterationPerformedGC timerSinceLastMajorGC = do
    40 40   timeSinceLastGC <- timerSinceLastMajorGC
    41 41   when (timeSinceLastGC < minGCInterval) $ do
    42 42   -- no need to check idle until we've passed the minGCInterval:
    skipped 6 lines
    49 49   let areIdle = gcs == gcs_prev
    50 50   areOverdue = timeSinceLastGC > maxNoGCInterval
    51 51   
    52  - -- a major GC was run since last iteration (cool!), reset timer:
    53 52   if
    54  - | major_gcs > major_gcs_prev -> do
    55  - startTimer >>= go gcs major_gcs
     53 + -- a major GC was run since last iteration (cool!), reset timer:
     54 + | major_gcs > major_gcs_prev -> do
     55 + startTimer >>= go gcs major_gcs False
    56 56   
    57  - -- we are idle and its a good time to do a GC, or we're overdue and must run a GC:
    58  - | areIdle || areOverdue -> do
    59  - when (areOverdue && not areIdle) $
    60  - logger $
    61  - UnstructuredLog LevelWarn $
    62  - "Overdue for a major GC: forcing one even though we don't appear to be idle"
    63  - performMajorGC
    64  - startTimer >>= go (gcs + 1) (major_gcs + 1)
     57 + -- we are idle and its a good time to do a GC, or we're overdue and must run a GC:
     58 + | areIdle || areOverdue -> do
     59 + -- If we performed a GC last time and nothing was promoted meantime
     60 + -- (minor GCs are the same) running a cheaper minor GC should
     61 + -- suffice to perform any new due finalizers:
     62 + if lastIterationPerformedGC && areIdle
     63 + then do
     64 + performMinorGC
     65 + startTimer >>= go (gcs + 1) major_gcs True
     66 + else do
     67 + when (areOverdue && not areIdle)
     68 + $ logger
     69 + $ UnstructuredLog LevelWarn
     70 + $ "Overdue for a major GC: forcing one even though we don't appear to be idle"
     71 + performMajorGC
     72 + startTimer >>= go (gcs + 1) (major_gcs + 1) True
    65 73   
    66 74   -- else keep the timer running, waiting for us to go idle:
    67 75   | otherwise -> do
    68 76   C.sleep idleInterval
    69  - go gcs major_gcs timerSinceLastMajorGC
     77 + go gcs major_gcs False timerSinceLastMajorGC
    70 78   
Please wait...
Page is in error, reload to recover