Skip to content

Commit 7ff02db

Browse files
committed
feat: add metric pgrst_jwt_cache_size_bytes in admin server
1 parent 9ae8854 commit 7ff02db

File tree

12 files changed

+77
-16
lines changed

12 files changed

+77
-16
lines changed

.github/workflows/test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,5 +128,5 @@ jobs:
128128
prefix: v
129129
- name: Run loadtest
130130
run: |
131-
postgrest-loadtest-against main ${{ steps.get-latest-tag.outputs.tag }}
131+
PGRST_BUILD_CABAL=1 postgrest-loadtest-against main ${{ steps.get-latest-tag.outputs.tag }}
132132
postgrest-loadtest-report >> "$GITHUB_STEP_SUMMARY"

docs/references/observability.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,20 @@ pgrst_db_pool_max
169169

170170
Max pool connections.
171171

172+
JWT Cache Metric
173+
----------------
174+
175+
Related to the :ref:`jwt_caching`.
176+
177+
pgrst_jwt_cache_size_bytes
178+
~~~~~~~~~~~~~~~~~~~~~~~~~~
179+
180+
======== =======
181+
**Type** Gauge
182+
======== =======
183+
184+
Approximate JWT cache size in bytes.
185+
172186
Traces
173187
======
174188

nix/overlays/haskell-packages.nix

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ let
5050
# jailbreak, because hspec limit for tests
5151
fuzzyset = prev.fuzzyset_0_2_4;
5252

53+
# TODO: Remove this once https://github.com/NixOS/nixpkgs/pull/375121
54+
# has made it to us.
55+
ghc-datasize = lib.markUnbroken prev.ghc-datasize;
56+
5357
hasql-pool = lib.dontCheck (prev.callHackageDirect
5458
{
5559
pkg = "hasql-pool";
@@ -69,6 +73,7 @@ let
6973
{
7074
postgresql = super.libpq;
7175
});
76+
7277
};
7378
in
7479
{

postgrest.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ library
108108
, either >= 4.4.1 && < 5.1
109109
, extra >= 1.7.0 && < 2.0
110110
, fuzzyset >= 0.2.4 && < 0.3
111+
, ghc-datasize >= 0.2.7 && < 0.3
112+
, ghc-heap >= 9.4 && < 9.9
111113
, gitrev >= 1.2 && < 1.4
112114
, hasql >= 1.6.1.1 && < 1.7
113115
, hasql-dynamic-statements >= 0.3.1 && < 0.4

src/PostgREST/App.hs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ module PostgREST.App
1515
, run
1616
) where
1717

18-
18+
import Control.Monad
1919
import Control.Monad.Except (liftEither)
2020
import Data.Either.Combinators (mapLeft)
2121
import Data.Maybe (fromJust)
@@ -41,8 +41,7 @@ import qualified PostgREST.Response as Response
4141
import qualified PostgREST.Unix as Unix (installSignalHandlers)
4242

4343
import PostgREST.ApiRequest (ApiRequest (..))
44-
import PostgREST.AppState (AppState)
45-
import PostgREST.Auth (AuthResult (..))
44+
import PostgREST.AppState (AppState, AuthResult (..))
4645
import PostgREST.Config (AppConfig (..), LogLevel (..))
4746
import PostgREST.Config.PgVersion (PgVersion (..))
4847
import PostgREST.Error (Error)

src/PostgREST/Auth.hs

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,17 @@ import Data.Either.Combinators (mapLeft)
4141
import Data.List (lookup)
4242
import Data.Time.Clock (UTCTime, nominalDiffTimeToSeconds)
4343
import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds)
44+
import GHC.DataSize (recursiveSizeNF)
4445
import System.Clock (TimeSpec (..))
4546
import System.IO.Unsafe (unsafePerformIO)
4647
import System.TimeIt (timeItT)
4748

48-
import PostgREST.AppState (AppState, AuthResult (..), getConfig,
49-
getJwtCache, getTime)
50-
import PostgREST.Config (AppConfig (..), FilterExp (..), JSPath,
51-
JSPathExp (..))
52-
import PostgREST.Error (Error (..))
49+
import PostgREST.AppState (AppState, AuthResult (..), getConfig,
50+
getJwtCache, getObserver, getTime)
51+
import PostgREST.Config (AppConfig (..), FilterExp (..), JSPath,
52+
JSPathExp (..))
53+
import PostgREST.Error (Error (..))
54+
import PostgREST.Observation (Observation (..))
5355

5456
import Protolude
5557

@@ -153,7 +155,7 @@ middleware appState app req respond = do
153155
let token = fromMaybe "" $ Wai.extractBearerAuth =<< lookup HTTP.hAuthorization (Wai.requestHeaders req)
154156
parseJwt = runExceptT $ parseToken conf token time >>= parseClaims conf
155157

156-
-- If DbPlanEnabled -> calculate JWT validation time
158+
-- If ServerTimingEnabled -> calculate JWT validation time
157159
-- If JwtCacheMaxLifetime -> cache JWT validation result
158160
req' <- case (configServerTimingEnabled conf, configJwtCacheMaxLifetime conf) of
159161
(True, 0) -> do
@@ -177,14 +179,24 @@ middleware appState app req respond = do
177179
-- | Used to retrieve and insert JWT to JWT Cache
178180
getJWTFromCache :: AppState -> ByteString -> Int -> IO (Either Error AuthResult) -> UTCTime -> IO (Either Error AuthResult)
179181
getJWTFromCache appState token maxLifetime parseJwt utc = do
180-
checkCache <- C.lookup (getJwtCache appState) token
182+
183+
checkCache <- C.lookup jwtCache token
181184
authResult <- maybe parseJwt (pure . Right) checkCache
182185

186+
-- if token not found, add to cache and increment cache size metric
183187
case (authResult,checkCache) of
184-
(Right res, Nothing) -> C.insert' (getJwtCache appState) (getTimeSpec res maxLifetime utc) token res
188+
(Right res, Nothing) -> do
189+
let tSpec = getTimeSpec res maxLifetime utc
190+
C.insert' jwtCache tSpec token res
191+
entrySize <- calcCacheEntrySizeInBytes (token, res, tSpec) -- adds to cache
192+
observer $ JWTCache entrySize
193+
185194
_ -> pure ()
186195

187196
return authResult
197+
where
198+
observer = getObserver appState
199+
jwtCache = getJwtCache appState
188200

189201
-- Used to extract JWT exp claim and add to JWT Cache
190202
getTimeSpec :: AuthResult -> Int -> UTCTime -> Maybe TimeSpec
@@ -196,6 +208,23 @@ getTimeSpec res maxLifetime utc = do
196208
Just (JSON.Number seconds) -> Just $ TimeSpec (sciToInt seconds - utcToSecs utc) 0
197209
_ -> Just $ TimeSpec (fromIntegral maxLifetime :: Int64) 0
198210

211+
-- | Calculate a single entry of JWT Cache Size in Bytes
212+
calcCacheEntrySizeInBytes :: (ByteString,AuthResult,Maybe TimeSpec) -> IO Int
213+
calcCacheEntrySizeInBytes entry = do
214+
sz <- getSize entry
215+
return $ fromIntegral sz
216+
where
217+
getSize :: (ByteString, AuthResult,Maybe TimeSpec) -> IO Word
218+
getSize (bs, ar, ts) = do
219+
keySize <- recursiveSizeNF bs
220+
arClaimsSize <- recursiveSizeNF $ authClaims ar
221+
arRoleSize <- recursiveSizeNF $ authRole ar
222+
timeSpecSize <- case ts of
223+
Just TimeSpec{..} -> liftA2 (+) (recursiveSizeNF sec) (recursiveSizeNF nsec)
224+
Nothing -> pure 0
225+
226+
return (keySize + arClaimsSize + arRoleSize + timeSpecSize)
227+
199228
authResultKey :: Vault.Key (Either Error AuthResult)
200229
authResultKey = unsafePerformIO Vault.newKey
201230
{-# NOINLINE authResultKey #-}

src/PostgREST/Logger.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ observationLogger loggerState logLevel obs = case obs of
8888
o@(HasqlPoolObs _) -> do
8989
when (logLevel >= LogDebug) $ do
9090
logWithZTime loggerState $ observationMessage o
91+
o@(JWTCache _) -> do
92+
when (logLevel >= LogDebug) $ do
93+
logWithZTime loggerState $ observationMessage o
9194
PoolRequest ->
9295
pure ()
9396
PoolRequestFullfilled ->

src/PostgREST/Metrics.hs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{-|
2-
Module : PostgREST.Logger
2+
Module : PostgREST.Metrics
33
Description : Metrics based on the Observation module. See Observation.hs.
44
-}
55
module PostgREST.Metrics
@@ -19,7 +19,7 @@ import PostgREST.Observation
1919
import Protolude
2020

2121
data MetricsState =
22-
MetricsState Counter Gauge Gauge Gauge (Vector Label1 Counter) Gauge
22+
MetricsState Counter Gauge Gauge Gauge (Vector Label1 Counter) Gauge Gauge
2323

2424
init :: Int -> IO MetricsState
2525
init configDbPoolSize = do
@@ -29,12 +29,13 @@ init configDbPoolSize = do
2929
poolMaxSize <- register $ gauge (Info "pgrst_db_pool_max" "Max pool connections")
3030
schemaCacheLoads <- register $ vector "status" $ counter (Info "pgrst_schema_cache_loads_total" "The total number of times the schema cache was loaded")
3131
schemaCacheQueryTime <- register $ gauge (Info "pgrst_schema_cache_query_time_seconds" "The query time in seconds of the last schema cache load")
32+
jwtCacheSize <- register $ gauge (Info "pgrst_jwt_cache_size_bytes" "The JWT cache size in bytes")
3233
setGauge poolMaxSize (fromIntegral configDbPoolSize)
33-
pure $ MetricsState poolTimeouts poolAvailable poolWaiting poolMaxSize schemaCacheLoads schemaCacheQueryTime
34+
pure $ MetricsState poolTimeouts poolAvailable poolWaiting poolMaxSize schemaCacheLoads schemaCacheQueryTime jwtCacheSize
3435

3536
-- Only some observations are used as metrics
3637
observationMetrics :: MetricsState -> ObservationHandler
37-
observationMetrics (MetricsState poolTimeouts poolAvailable poolWaiting _ schemaCacheLoads schemaCacheQueryTime) obs = case obs of
38+
observationMetrics (MetricsState poolTimeouts poolAvailable poolWaiting _ schemaCacheLoads schemaCacheQueryTime jwtCacheSize) obs = case obs of
3839
(PoolAcqTimeoutObs _) -> do
3940
incCounter poolTimeouts
4041
(HasqlPoolObs (SQL.ConnectionObservation _ status)) -> case status of
@@ -54,6 +55,8 @@ observationMetrics (MetricsState poolTimeouts poolAvailable poolWaiting _ schema
5455
setGauge schemaCacheQueryTime resTime
5556
SchemaCacheErrorObs _ -> do
5657
withLabel schemaCacheLoads "FAIL" incCounter
58+
JWTCache entrySize -> do
59+
addGauge jwtCacheSize (fromIntegral entrySize)
5760
_ ->
5861
pure ()
5962

src/PostgREST/Observation.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ data Observation
5757
| HasqlPoolObs SQL.Observation
5858
| PoolRequest
5959
| PoolRequestFullfilled
60+
| JWTCache Int
6061

6162
data ObsFatalError = ServerAuthError | ServerPgrstBug | ServerError42P05 | ServerError08P01
6263

@@ -138,6 +139,7 @@ observationMessage = \case
138139
SQL.ReleaseConnectionTerminationReason -> "release"
139140
SQL.NetworkErrorConnectionTerminationReason _ -> "network error" -- usage error is already logged, no need to repeat the same message.
140141
)
142+
JWTCache sz-> "The JWT Cache size increased to " <> show sz <> " bytes"
141143
_ -> mempty
142144
where
143145
showMillis :: Double -> Text

stack-21.7.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ nix:
1919
extra-deps:
2020
- configurator-pg-0.2.10
2121
- fuzzyset-0.2.4
22+
- ghc-datasize-0.2.7
2223
- hasql-notifications-0.2.2.0
2324
- hasql-pool-1.0.1
2425
- postgresql-libpq-0.10.1.0

0 commit comments

Comments
 (0)