This adds a new log line that shows each schema cache query time individually, only on `log-level=debug`. Like so: ``` $ PGRST_LOG_LEVEL=debug postgrest-with-pg-17 -f test/spec/fixtures/load.sql postgrest-run .... 10/Apr/2026:21:48:45 -0500: Schema cache queried in 192.2 milliseconds 10/Apr/2026:21:48:45 -0500: tables: 72.027 ms, keydeps: 20.118 ms, rels: 6.189 ms, funcs: 35.010 ms, comprels: 4.319 ms, dreps: 1.614 ms, mhandlers: 7.419 ms, tzones: 43.025 ms ``` This helps debug specific schema cache queries being slow like on https://github.com/PostgREST/postgrest/issues/4613#issuecomment-4210191065 and https://github.com/PostgREST/postgrest/issues/3046#issuecomment-3469059948. It also closes https://github.com/PostgREST/postgrest/issues/3215, which main motivation was to find out which query is slow. Implementation details --------------------- To time each query inside a transaction in pure SQL, we do: ```sql -- start timer select set_config('pgrst.tmp_x', clock_timestamp()::text, false); -- run the query select <query> -- end timer select set_config('pgrst.tmp_x', (clock_timestamp() - current_setting('pgrst.tmp_x', false)::timestamptz)::text, false); -- .... repeated for every query -- at the end we capture all the timings with select extract('milliseconds' from current_setting('pgrst.tmp_x', false)::interval), extract(..; ``` Considerations -------------- Only added this on `log-level=debug` because while the queries are fast and the data is valuable, it triples the amount of queries we run during schema cache refresh, which could be troublesome on slow networks. It's possible to reduce the amount of queries by starting and stopping timers in one statement, but this would still double the amount of queries and makes the code messy, doesn't seem worth it. Also it would pollute pg_stat_statements, it's only required to debug certain extreme cases anyway.
67 lines
2.5 KiB
Haskell
67 lines
2.5 KiB
Haskell
{-# LANGUAGE DeriveGeneric #-}
|
|
{-|
|
|
Module : PostgREST.Observation
|
|
Description : This module holds an Observation type which is the core of Observability for PostgREST.
|
|
The Observation and ObservationHandler (the observer) are abstractions that allow centralizing logging and metrics concerns,
|
|
only observer calls with an Observation constructor are applied at different parts in the codebase.
|
|
The Logger and Metrics modules then decide which observations to expose. Not all observations need to be logged nor all correspond to a metric.
|
|
-}
|
|
module PostgREST.Observation
|
|
( Observation(..)
|
|
, ObsFatalError(..)
|
|
, ObservationHandler
|
|
) where
|
|
|
|
import qualified Hasql.Connection as SQL
|
|
import qualified Hasql.Pool as SQL
|
|
import qualified Hasql.Pool.Observation as SQL
|
|
import Network.HTTP.Types.Status (Status)
|
|
import PostgREST.Config.PgVersion
|
|
import PostgREST.Query (MainQuery)
|
|
import PostgREST.SchemaCache (QueryTimings)
|
|
|
|
import Protolude hiding (toList)
|
|
|
|
data Observation
|
|
= AdminStartObs Text
|
|
| AppStartObs ByteString
|
|
| AppServerAddressObs Text
|
|
| ExitUnsupportedPgVersion PgVersion PgVersion
|
|
| ExitDBNoRecoveryObs
|
|
| ExitDBFatalError ObsFatalError SQL.UsageError
|
|
| DBConnectedObs Text
|
|
| SchemaCacheEmptyObs
|
|
| SchemaCacheErrorObs (NonEmpty Text) [Text] SQL.UsageError
|
|
| SchemaCacheQueriedObs Double (Maybe QueryTimings)
|
|
| SchemaCacheLoadedObs Double Text
|
|
| ConnectionRetryObs Int
|
|
| DBListenStart (Maybe ByteString) (Maybe ByteString) Text Text -- host, port, version string, channel
|
|
| DBListenFail Text (Either SQL.ConnectionError SomeException)
|
|
| DBListenRetry Int
|
|
| DBListenBugHint -- https://github.com/PostgREST/postgrest/issues/3147
|
|
| DBListenerGotSCacheMsg ByteString
|
|
| DBListenerGotConfigMsg ByteString
|
|
| DBListenerConnectionCleanupFail SomeException
|
|
| QueryObs MainQuery Status
|
|
| ConfigReadErrorObs SQL.UsageError
|
|
| ConfigInvalidObs Text
|
|
| ConfigSucceededObs
|
|
| QueryRoleSettingsErrorObs SQL.UsageError
|
|
| QueryErrorCodeHighObs SQL.UsageError
|
|
| QueryPgVersionError SQL.UsageError
|
|
| PoolInit Int
|
|
| PoolAcqTimeoutObs
|
|
| HasqlPoolObs SQL.Observation
|
|
| PoolRequest
|
|
| PoolRequestFullfilled
|
|
| PoolFlushed
|
|
| JwtCacheLookup Bool
|
|
| JwtCacheEviction
|
|
| TerminationUnixSignalObs Text
|
|
| WarpServerObs Text
|
|
deriving (Generic)
|
|
|
|
data ObsFatalError = ServerAuthError | ServerPgrstBug | ServerError42P05 | ServerError08P01
|
|
|
|
type ObservationHandler = Observation -> IO ()
|