Skip to content

Commit 4e007c2

Browse files
committed
add(metrics): expose db pool flushes counter
Emit a dedicated PoolFlushed observation when the DB pool is released during schema cache reload and expose it as pgrst_db_pool_flushes_total.
1 parent 1f5219e commit 4e007c2

9 files changed

Lines changed: 83 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ All notable changes to this project will be documented in this file. From versio
1212
- Log host, port and pg version of listener database connection by @mkleczek in #4617 #4618
1313
- Optimize requests with `Prefer: count=exact` that do not use ranges or `db-max-rows` by @laurenceisla in #3957
1414
+ Removed unnecessary double count when building the `Content-Range`.
15+
- Expose db pool flushes counter by @mkleczek in #4658
1516

1617
### Changed
1718

docs/references/observability.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,15 @@ pgrst_db_pool_timeouts_total
177177

178178
The total number of pool connection timeouts.
179179

180+
pgrst_db_pool_flushes_total
181+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
182+
183+
======== =======
184+
**Type** Counter
185+
======== =======
186+
187+
The total number of times the pool was flushed.
188+
180189
pgrst_db_pool_available
181190
~~~~~~~~~~~~~~~~~~~~~~~
182191

postgrest.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ test-suite spec
218218
Feature.ConcurrentSpec
219219
Feature.CorsSpec
220220
Feature.ExtraSearchPathSpec
221+
Feature.MetricsSpec
221222
Feature.NoSuperuserSpec
222223
Feature.ObservabilitySpec
223224
Feature.OpenApi.DisabledOpenApiSpec

src/PostgREST/AppState.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,9 @@ usePool AppState{stateObserver=observer, stateMainThreadId=mainThreadId, ..} ses
271271
-- | Flush the connection pool so that any future use of the pool will
272272
-- use connections freshly established after this call.
273273
flushPool :: AppState -> IO ()
274-
flushPool AppState{..} = SQL.release statePool
274+
flushPool AppState{..} = do
275+
SQL.release statePool
276+
stateObserver PoolFlushed
275277

276278
-- | Destroy the pool on shutdown.
277279
destroyPool :: AppState -> IO ()

src/PostgREST/Logger.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ observationLogger loggerState logLevel obs = case obs of
102102
o@PoolRequestFullfilled ->
103103
when (logLevel >= LogDebug) $ do
104104
logWithZTime loggerState $ observationMessage o
105+
o@PoolFlushed ->
106+
when (logLevel >= LogDebug) $ do
107+
logWithZTime loggerState $ observationMessage o
105108
o@JwtCacheEviction ->
106109
when (logLevel >= LogDebug) $ do
107110
logWithZTime loggerState $ observationMessage o

src/PostgREST/Metrics.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import Protolude
2222
data MetricsState =
2323
MetricsState {
2424
poolTimeouts :: Counter,
25+
poolFlushes :: Counter,
2526
poolAvailable :: Gauge,
2627
poolWaiting :: Gauge,
2728
poolMaxSize :: Gauge,
@@ -36,6 +37,7 @@ init :: Int -> IO MetricsState
3637
init configDbPoolSize = do
3738
metricState <- MetricsState <$>
3839
register (counter (Info "pgrst_db_pool_timeouts_total" "The total number of pool connection timeouts")) <*>
40+
register (counter (Info "pgrst_db_pool_flushes_total" "The total number of times the pool was flushed")) <*>
3941
register (gauge (Info "pgrst_db_pool_available" "Available connections in the pool")) <*>
4042
register (gauge (Info "pgrst_db_pool_waiting" "Requests waiting to acquire a pool connection")) <*>
4143
register (gauge (Info "pgrst_db_pool_max" "Max pool connections")) <*>
@@ -64,6 +66,8 @@ observationMetrics MetricsState{..} obs = case obs of
6466
incGauge poolWaiting
6567
PoolRequestFullfilled ->
6668
decGauge poolWaiting
69+
PoolFlushed ->
70+
incCounter poolFlushes
6771
SchemaCacheLoadedObs resTime -> do
6872
withLabel schemaCacheLoads "SUCCESS" incCounter
6973
setGauge schemaCacheQueryTime resTime

src/PostgREST/Observation.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ data Observation
6363
| HasqlPoolObs SQL.Observation
6464
| PoolRequest
6565
| PoolRequestFullfilled
66+
| PoolFlushed
6667
| JwtCacheLookup Bool
6768
| JwtCacheEviction
6869
| WarpErrorObs Text
@@ -157,6 +158,8 @@ observationMessage = \case
157158
"Trying to borrow a connection from pool"
158159
PoolRequestFullfilled ->
159160
"Borrowed a connection from the pool"
161+
PoolFlushed ->
162+
"Database connection pool flushed"
160163
JwtCacheLookup _ ->
161164
"Looked up a JWT in JWT cache"
162165
JwtCacheEviction ->

test/spec/Feature/MetricsSpec.hs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{-# LANGUAGE DataKinds #-}
2+
{-# LANGUAGE LambdaCase #-}
3+
{-# LANGUAGE TypeApplications #-}
4+
module Feature.MetricsSpec
5+
6+
where
7+
8+
import Network.Wai (Application)
9+
10+
import qualified PostgREST.AppState as AppState
11+
import PostgREST.Metrics (MetricsState (..))
12+
import PostgREST.Observation
13+
import Protolude
14+
import SpecHelper
15+
import Test.Hspec (SpecWith, describe,
16+
expectationFailure, it)
17+
import Test.Hspec.Wai (getState)
18+
19+
untilM :: Int -> (a -> Bool) -> IO a -> IO (Maybe a)
20+
untilM timeout cond act = rightToMaybe <$> race (threadDelay timeout) (fix $
21+
\loop -> do
22+
value <- act
23+
if cond value then
24+
pure value
25+
else
26+
loop)
27+
28+
waitForSchemaReload :: Int -> IO Observation -> IO (Maybe Observation)
29+
waitForSchemaReload timeout = untilM timeout $ \case
30+
SchemaCacheLoadedObs _ -> True
31+
_ -> False
32+
33+
spec :: SpecWith ((MetricsState, AppState.AppState, IO Observation), Application)
34+
spec = describe "Server started with metrics enabled" $
35+
it "Should increase pool flushes metric when schema cache is reloaded" $ do
36+
(metrics, appState, readObs) <- getState
37+
38+
checkState' metrics
39+
40+
[
41+
expectCounter @"poolFlushes" (+1)
42+
] $
43+
44+
liftIO $ do
45+
AppState.schemaCacheLoader appState
46+
waitForSchemaReload 1000000 readObs >>=
47+
maybe (expectationFailure "Timeout waiting for schema reloading") mempty

test/spec/Main.hs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import qualified Feature.Auth.NoJwtSecretSpec
2929
import qualified Feature.ConcurrentSpec
3030
import qualified Feature.CorsSpec
3131
import qualified Feature.ExtraSearchPathSpec
32+
import qualified Feature.MetricsSpec
3233
import qualified Feature.NoSuperuserSpec
3334
import qualified Feature.ObservabilitySpec
3435
import qualified Feature.OpenApi.DisabledOpenApiSpec
@@ -87,6 +88,7 @@ main = do
8788
sockets <- AppState.initSockets testCfg
8889
loggerState <- Logger.init
8990
metricsState <- Metrics.init (configDbPoolSize testCfg)
91+
obsChan <- newChan
9092

9193
let
9294
initApp sCache st config = do
@@ -95,6 +97,12 @@ main = do
9597
AppState.putSchemaCache appState (Just sCache)
9698
return (st, postgrest (configLogLevel config) appState (pure ()))
9799

100+
initMetricsApp sCache config = do
101+
appState <- AppState.initWithPool sockets pool config loggerState metricsState (Metrics.observationMetrics metricsState <> writeChan obsChan)
102+
AppState.putPgVersion appState actualPgVersion
103+
AppState.putSchemaCache appState (Just sCache)
104+
return ((metricsState, appState, readChan obsChan), postgrest (configLogLevel config) appState (pure ()))
105+
98106
-- For tests that run with the same schema cache
99107
app = initApp baseSchemaCache ()
100108

@@ -123,6 +131,7 @@ main = do
123131
obsApp = app testObservabilityCfg
124132
serverTiming = app testCfgServerTiming
125133
aggregatesEnabled = app testCfgAggregatesEnabled
134+
metricsApp = initMetricsApp baseSchemaCache testCfg
126135

127136
extraSearchPathApp = appDbs testCfgExtraSearchPath
128137
unicodeApp = appDbs testUnicodeCfg
@@ -278,6 +287,9 @@ main = do
278287
before (initApp baseSchemaCache metricsState testCfgJwtCache) $
279288
describe "Feature.Auth.JwtCacheSpec" Feature.Auth.JwtCacheSpec.spec
280289

290+
before metricsApp $
291+
describe "Feature.MetricsSpec" Feature.MetricsSpec.spec
292+
281293
where
282294
loadSCache pool conf =
283295
either (panic.show) id <$> P.use pool (HT.transaction HT.ReadCommitted HT.Read $ querySchemaCache conf)

0 commit comments

Comments
 (0)