Skip to content

Commit 4d85023

Browse files
authored
fix: purge JWT cache asynchronously in a separate thread
Otherwise performance was reduced unnecessarily.
1 parent 58b5dff commit 4d85023

1 file changed

Lines changed: 22 additions & 7 deletions

File tree

src/PostgREST/Auth/JwtCache.hs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import qualified Data.Aeson.KeyMap as KM
1717
import qualified Data.Cache as C
1818
import qualified Data.Scientific as Sci
1919

20+
import Control.Debounce
21+
2022
import Data.Time.Clock (UTCTime, nominalDiffTimeToSeconds)
2123
import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds)
2224
import System.Clock (TimeSpec (..))
@@ -26,19 +28,30 @@ import PostgREST.Error (Error (..))
2628

2729
import Protolude
2830

29-
newtype JwtCacheState = JwtCacheState
30-
{ jwtCache :: C.Cache ByteString AuthResult
31+
-- | JWT Cache and IO action that triggers purging old entries from the cache
32+
data JwtCacheState = JwtCacheState
33+
{ jwtCache :: C.Cache ByteString AuthResult
34+
, purgeCache :: IO ()
3135
}
3236

3337
-- | Initialize JwtCacheState
3438
init :: IO JwtCacheState
3539
init = do
3640
cache <- C.newCache Nothing -- no default expiration
37-
return $ JwtCacheState cache
41+
-- purgeExpired has O(n^2) complexity
42+
-- so we wrap it in debounce to make sure it:
43+
-- 1) is executed asynchronously
44+
-- 2) only a single purge operation is running at a time
45+
debounce <- mkDebounce defaultDebounceSettings
46+
-- debounceFreq is set to default 1 second
47+
{ debounceAction = C.purgeExpired cache
48+
, debounceEdge = leadingEdge
49+
}
50+
pure $ JwtCacheState cache debounce
3851

3952
-- | Used to retrieve and insert JWT to JWT Cache
4053
lookupJwtCache :: JwtCacheState -> ByteString -> Int -> IO (Either Error AuthResult) -> UTCTime -> IO (Either Error AuthResult)
41-
lookupJwtCache JwtCacheState{jwtCache} token maxLifetime parseJwt utc = do
54+
lookupJwtCache JwtCacheState{jwtCache, purgeCache} token maxLifetime parseJwt utc = do
4255
checkCache <- C.lookup jwtCache token
4356
authResult <- maybe parseJwt (pure . Right) checkCache
4457

@@ -59,12 +72,14 @@ lookupJwtCache JwtCacheState{jwtCache} token maxLifetime parseJwt utc = do
5972

6073
let timeSpec = getTimeSpec res maxLifetime utc
6174

62-
-- purge expired cache entries
63-
C.purgeExpired jwtCache
64-
6575
-- insert new cache entry
6676
C.insert' jwtCache (Just timeSpec) token res
6777

78+
-- Execute IO action to purge the cache
79+
-- It is assumed this action returns immidiately
80+
-- so that request processing is not blocked.
81+
purgeCache
82+
6883
_ -> pure ()
6984

7085
return authResult

0 commit comments

Comments
 (0)