Skip to content

Commit df87ce4

Browse files
mkleczektaimoorzaeem
authored andcommitted
add: log pool flushes
Emit a dedicated PoolFlushed observation when the DB pool is released during schema cache reload.
1 parent d369d2c commit df87ce4

7 files changed

Lines changed: 74 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. From versio
44

55
## Unreleased
66

7+
### Added
8+
9+
- Log when the pool is released during schema cache reload on `log-level=debug` by @mkleczek in #4668
10+
711
## [14.9] - 2026-04-10
812

913
### Added

postgrest.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ test-suite observability
304304
other-modules: ObsHelper
305305
Observation.JwtCache
306306
Observation.MetricsSpec
307+
Observation.SchemaCacheSpec
307308
build-depends: base >= 4.9 && < 4.20
308309
, base64-bytestring >= 1 && < 1.3
309310
, bytestring >= 0.10.8 && < 0.13

src/PostgREST/AppState.hs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,14 @@ usePool AppState{stateObserver=observer, stateMainThreadId=mainThreadId, ..} ses
219219

220220
-- | Flush the connection pool so that any future use of the pool will
221221
-- use connections freshly established after this call.
222+
-- | Emits PoolFlushed observation
222223
flushPool :: AppState -> IO ()
223-
flushPool AppState{..} = SQL.release statePool
224+
flushPool AppState{..} = do
225+
SQL.release statePool
226+
stateObserver PoolFlushed
224227

225228
-- | Destroy the pool on shutdown.
229+
-- | Differs from flushPool in not emiting PoolFlushed observation.
226230
destroyPool :: AppState -> IO ()
227231
destroyPool AppState{..} = SQL.release statePool
228232

src/PostgREST/Logger.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ observationLogger loggerState logLevel obs = case obs of
110110
o@PoolRequestFullfilled ->
111111
when (logLevel >= LogDebug) $ do
112112
logWithZTime loggerState $ observationMessage o
113+
o@PoolFlushed ->
114+
when (logLevel >= LogDebug) $ do
115+
logWithZTime loggerState $ observationMessage o
113116
o@JwtCacheEviction ->
114117
when (logLevel >= LogDebug) $ do
115118
logWithZTime loggerState $ observationMessage o

src/PostgREST/Observation.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ data Observation
6464
| HasqlPoolObs SQL.Observation
6565
| PoolRequest
6666
| PoolRequestFullfilled
67+
| PoolFlushed
6768
| JwtCacheLookup Bool
6869
| JwtCacheEviction
6970
| TerminationUnixSignalObs Text
@@ -161,6 +162,8 @@ observationMessage = \case
161162
"Trying to borrow a connection from pool"
162163
PoolRequestFullfilled ->
163164
"Borrowed a connection from the pool"
165+
PoolFlushed ->
166+
"Database connection pool flushed"
164167
JwtCacheLookup _ ->
165168
"Looked up a JWT in JWT cache"
166169
JwtCacheEviction ->

test/observability/Main.hs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ import PostgREST.SchemaCache (querySchemaCache)
1717
import qualified Observation.JwtCache
1818
import qualified Observation.MetricsSpec
1919

20-
import ObsHelper
21-
import PostgREST.Observation (Observation (HasqlPoolObs))
22-
import Protolude hiding (toList, toS)
23-
import Test.Hspec
20+
import qualified Observation.SchemaCacheSpec
21+
import ObsHelper
22+
import PostgREST.Observation (Observation (HasqlPoolObs))
23+
import Protolude hiding (toList, toS)
24+
import Test.Hspec
2425

2526
main :: IO ()
2627
main = do
@@ -64,6 +65,8 @@ main = do
6465
describe "Observation.JwtCacheObs" Observation.JwtCache.spec
6566
before (initApp baseSchemaCache testCfg) $
6667
describe "Feature.MetricsSpec" Observation.MetricsSpec.spec
68+
before (initApp baseSchemaCache testCfg) $
69+
describe "Feature.SchemaCacheSpec" Observation.SchemaCacheSpec.spec
6770

6871
where
6972
loadSCache pool conf =
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{-# LANGUAGE DataKinds #-}
2+
{-# LANGUAGE MonadComprehensions #-}
3+
{-# LANGUAGE NamedFieldPuns #-}
4+
module Observation.SchemaCacheSpec where
5+
6+
import Network.Wai (Application)
7+
import ObsHelper
8+
import qualified PostgREST.AppState as AppState
9+
import PostgREST.Config (configDbSchemas)
10+
import PostgREST.Observation
11+
import Protolude
12+
import Test.Hspec (SpecWith, describe, it)
13+
import Test.Hspec.Wai (getState)
14+
15+
spec :: SpecWith (SpecState, Application)
16+
spec = describe "Server started with metrics enabled" $ do
17+
18+
it "Should emit PoolFlushed, SchemaCacheQueriedObs and SchemaCacheLoadedObs when schema cache is reloaded" $ do
19+
SpecState{specAppState = appState, specObsChan} <- getState
20+
let waitFor = waitForObs specObsChan
21+
22+
liftIO $ do
23+
AppState.schemaCacheLoader appState
24+
25+
waitFor (1 * sec) "PoolFlushed" $ \x -> [ o | o@PoolFlushed <- pure x ]
26+
waitFor (1 * sec) "SchemaCacheQueriedObs" $ \x -> [ o | o@SchemaCacheQueriedObs{} <- pure x ]
27+
waitFor (1 * sec) "SchemaCacheLoadedObs" $ \x -> [ o | o@SchemaCacheLoadedObs{} <- pure x ]
28+
29+
30+
it "Should flush pool multiple times when schema reloading retries" $ do
31+
SpecState{specAppState = appState, specObsChan} <- getState
32+
let waitFor = waitForObs specObsChan
33+
34+
liftIO $ do
35+
AppState.getConfig appState >>= \cfg -> do
36+
AppState.putConfig appState $ cfg { configDbSchemas = pure "bad_schema" }
37+
AppState.schemaCacheLoader appState
38+
39+
waitFor (1 * sec) "PoolFlushed 1" $ \x -> [ o | o@PoolFlushed <- pure x ]
40+
waitFor (1 * sec) "SchemaCacheErrorObs" $ \x -> [ o | o@SchemaCacheErrorObs{} <- pure x ]
41+
42+
-- Restore configuration
43+
AppState.putConfig appState cfg
44+
45+
-- Wait for 2 seconds so that retry can happen
46+
waitFor (2 * sec) "PoolFlushed 2" $ \x -> [ o | o@PoolFlushed <- pure x ]
47+
waitFor (1 * sec) "SchemaCacheQueriedObs" $ \x -> [ o | o@SchemaCacheQueriedObs{} <- pure x ]
48+
waitFor (1 * sec) "SchemaCacheLoadedObs" $ \x -> [ o | o@SchemaCacheLoadedObs{} <- pure x ]
49+
50+
where
51+
sec = 1000000

0 commit comments

Comments
 (0)