@@ -17,6 +17,8 @@ import qualified Data.Aeson.KeyMap as KM
1717import qualified Data.Cache as C
1818import qualified Data.Scientific as Sci
1919
20+ import Control.Debounce
21+
2022import Data.Time.Clock (UTCTime , nominalDiffTimeToSeconds )
2123import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds )
2224import System.Clock (TimeSpec (.. ))
@@ -26,19 +28,30 @@ import PostgREST.Error (Error (..))
2628
2729import 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
3438init :: IO JwtCacheState
3539init = 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
4053lookupJwtCache :: 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