add: log pool flushes

Emit a dedicated PoolFlushed observation when the DB pool is released during schema cache reload.
This commit is contained in:
Michał Kłeczek
2026-04-16 11:18:44 +05:00
committed by Taimoor Zaeem
parent d369d2c41e
commit df87ce46ed
7 changed files with 74 additions and 5 deletions
+4
View File
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. From versio
## Unreleased ## Unreleased
### Added
- Log when the pool is released during schema cache reload on `log-level=debug` by @mkleczek in #4668
## [14.9] - 2026-04-10 ## [14.9] - 2026-04-10
### Added ### Added
+1
View File
@@ -304,6 +304,7 @@ test-suite observability
other-modules: ObsHelper other-modules: ObsHelper
Observation.JwtCache Observation.JwtCache
Observation.MetricsSpec Observation.MetricsSpec
Observation.SchemaCacheSpec
build-depends: base >= 4.9 && < 4.20 build-depends: base >= 4.9 && < 4.20
, base64-bytestring >= 1 && < 1.3 , base64-bytestring >= 1 && < 1.3
, bytestring >= 0.10.8 && < 0.13 , bytestring >= 0.10.8 && < 0.13
+5 -1
View File
@@ -219,10 +219,14 @@ usePool AppState{stateObserver=observer, stateMainThreadId=mainThreadId, ..} ses
-- | Flush the connection pool so that any future use of the pool will -- | Flush the connection pool so that any future use of the pool will
-- use connections freshly established after this call. -- use connections freshly established after this call.
-- | Emits PoolFlushed observation
flushPool :: AppState -> IO () flushPool :: AppState -> IO ()
flushPool AppState{..} = SQL.release statePool flushPool AppState{..} = do
SQL.release statePool
stateObserver PoolFlushed
-- | Destroy the pool on shutdown. -- | Destroy the pool on shutdown.
-- | Differs from flushPool in not emiting PoolFlushed observation.
destroyPool :: AppState -> IO () destroyPool :: AppState -> IO ()
destroyPool AppState{..} = SQL.release statePool destroyPool AppState{..} = SQL.release statePool
+3
View File
@@ -110,6 +110,9 @@ observationLogger loggerState logLevel obs = case obs of
o@PoolRequestFullfilled -> o@PoolRequestFullfilled ->
when (logLevel >= LogDebug) $ do when (logLevel >= LogDebug) $ do
logWithZTime loggerState $ observationMessage o logWithZTime loggerState $ observationMessage o
o@PoolFlushed ->
when (logLevel >= LogDebug) $ do
logWithZTime loggerState $ observationMessage o
o@JwtCacheEviction -> o@JwtCacheEviction ->
when (logLevel >= LogDebug) $ do when (logLevel >= LogDebug) $ do
logWithZTime loggerState $ observationMessage o logWithZTime loggerState $ observationMessage o
+3
View File
@@ -64,6 +64,7 @@ data Observation
| HasqlPoolObs SQL.Observation | HasqlPoolObs SQL.Observation
| PoolRequest | PoolRequest
| PoolRequestFullfilled | PoolRequestFullfilled
| PoolFlushed
| JwtCacheLookup Bool | JwtCacheLookup Bool
| JwtCacheEviction | JwtCacheEviction
| TerminationUnixSignalObs Text | TerminationUnixSignalObs Text
@@ -161,6 +162,8 @@ observationMessage = \case
"Trying to borrow a connection from pool" "Trying to borrow a connection from pool"
PoolRequestFullfilled -> PoolRequestFullfilled ->
"Borrowed a connection from the pool" "Borrowed a connection from the pool"
PoolFlushed ->
"Database connection pool flushed"
JwtCacheLookup _ -> JwtCacheLookup _ ->
"Looked up a JWT in JWT cache" "Looked up a JWT in JWT cache"
JwtCacheEviction -> JwtCacheEviction ->
+7 -4
View File
@@ -17,10 +17,11 @@ import PostgREST.SchemaCache (querySchemaCache)
import qualified Observation.JwtCache import qualified Observation.JwtCache
import qualified Observation.MetricsSpec import qualified Observation.MetricsSpec
import ObsHelper import qualified Observation.SchemaCacheSpec
import PostgREST.Observation (Observation (HasqlPoolObs)) import ObsHelper
import Protolude hiding (toList, toS) import PostgREST.Observation (Observation (HasqlPoolObs))
import Test.Hspec import Protolude hiding (toList, toS)
import Test.Hspec
main :: IO () main :: IO ()
main = do main = do
@@ -64,6 +65,8 @@ main = do
describe "Observation.JwtCacheObs" Observation.JwtCache.spec describe "Observation.JwtCacheObs" Observation.JwtCache.spec
before (initApp baseSchemaCache testCfg) $ before (initApp baseSchemaCache testCfg) $
describe "Feature.MetricsSpec" Observation.MetricsSpec.spec describe "Feature.MetricsSpec" Observation.MetricsSpec.spec
before (initApp baseSchemaCache testCfg) $
describe "Feature.SchemaCacheSpec" Observation.SchemaCacheSpec.spec
where where
loadSCache pool conf = loadSCache pool conf =
@@ -0,0 +1,51 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE MonadComprehensions #-}
{-# LANGUAGE NamedFieldPuns #-}
module Observation.SchemaCacheSpec where
import Network.Wai (Application)
import ObsHelper
import qualified PostgREST.AppState as AppState
import PostgREST.Config (configDbSchemas)
import PostgREST.Observation
import Protolude
import Test.Hspec (SpecWith, describe, it)
import Test.Hspec.Wai (getState)
spec :: SpecWith (SpecState, Application)
spec = describe "Server started with metrics enabled" $ do
it "Should emit PoolFlushed, SchemaCacheQueriedObs and SchemaCacheLoadedObs when schema cache is reloaded" $ do
SpecState{specAppState = appState, specObsChan} <- getState
let waitFor = waitForObs specObsChan
liftIO $ do
AppState.schemaCacheLoader appState
waitFor (1 * sec) "PoolFlushed" $ \x -> [ o | o@PoolFlushed <- pure x ]
waitFor (1 * sec) "SchemaCacheQueriedObs" $ \x -> [ o | o@SchemaCacheQueriedObs{} <- pure x ]
waitFor (1 * sec) "SchemaCacheLoadedObs" $ \x -> [ o | o@SchemaCacheLoadedObs{} <- pure x ]
it "Should flush pool multiple times when schema reloading retries" $ do
SpecState{specAppState = appState, specObsChan} <- getState
let waitFor = waitForObs specObsChan
liftIO $ do
AppState.getConfig appState >>= \cfg -> do
AppState.putConfig appState $ cfg { configDbSchemas = pure "bad_schema" }
AppState.schemaCacheLoader appState
waitFor (1 * sec) "PoolFlushed 1" $ \x -> [ o | o@PoolFlushed <- pure x ]
waitFor (1 * sec) "SchemaCacheErrorObs" $ \x -> [ o | o@SchemaCacheErrorObs{} <- pure x ]
-- Restore configuration
AppState.putConfig appState cfg
-- Wait for 2 seconds so that retry can happen
waitFor (2 * sec) "PoolFlushed 2" $ \x -> [ o | o@PoolFlushed <- pure x ]
waitFor (1 * sec) "SchemaCacheQueriedObs" $ \x -> [ o | o@SchemaCacheQueriedObs{} <- pure x ]
waitFor (1 * sec) "SchemaCacheLoadedObs" $ \x -> [ o | o@SchemaCacheLoadedObs{} <- pure x ]
where
sec = 1000000