feat: add metric label for scache load

localhost:3001/metrics now includes:

pgrst_schema_cache_loads_total{status="FAIL"} 352.0
pgrst_schema_cache_loads_total{status="SUCCESS"} 3.0

This allows testing the failure case on:

https://github.com/PostgREST/postgrest/issues/3424#issuecomment-2104904910
This commit is contained in:
steve-chavez
2024-05-14 17:57:21 -05:00
committed by Steve Chavez
parent 05447bae33
commit d25df459ea
3 changed files with 63 additions and 22 deletions
+3 -2
View File
@@ -109,9 +109,10 @@ The query time in seconds of the last schema cache load.
pgrst_schema_cache_loads_total pgrst_schema_cache_loads_total
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
======== ======= ========== ==========================
**Type** Counter **Type** Counter
======== ======= **Labels** ``status``: SUCCESS | FAIL
========== ==========================
The total number of times the schema cache was loaded. The total number of times the schema cache was loaded.
+20 -18
View File
@@ -8,47 +8,49 @@ module PostgREST.Metrics
import qualified Data.ByteString.Lazy as LBS import qualified Data.ByteString.Lazy as LBS
import qualified Hasql.Pool.Observation as SQL import qualified Hasql.Pool.Observation as SQL
import qualified Prometheus as Prom import Prometheus
import PostgREST.Observation import PostgREST.Observation
import Protolude import Protolude
data MetricsState = data MetricsState =
MetricsState Prom.Counter Prom.Gauge Prom.Gauge Prom.Gauge Prom.Counter Prom.Gauge MetricsState Counter Gauge Gauge Gauge (Vector Label1 Counter) Gauge
init :: Int -> IO MetricsState init :: Int -> IO MetricsState
init configDbPoolSize = do init configDbPoolSize = do
poolTimeouts <- Prom.register $ Prom.counter (Prom.Info "pgrst_db_pool_timeouts_total" "The total number of pool connection timeouts") poolTimeouts <- register $ counter (Info "pgrst_db_pool_timeouts_total" "The total number of pool connection timeouts")
poolAvailable <- Prom.register $ Prom.gauge (Prom.Info "pgrst_db_pool_available" "Available connections in the pool") poolAvailable <- register $ gauge (Info "pgrst_db_pool_available" "Available connections in the pool")
poolWaiting <- Prom.register $ Prom.gauge (Prom.Info "pgrst_db_pool_waiting" "Requests waiting to acquire a pool connection") poolWaiting <- register $ gauge (Info "pgrst_db_pool_waiting" "Requests waiting to acquire a pool connection")
poolMaxSize <- Prom.register $ Prom.gauge (Prom.Info "pgrst_db_pool_max" "Max pool connections") poolMaxSize <- register $ gauge (Info "pgrst_db_pool_max" "Max pool connections")
schemaCacheLoads <- Prom.register $ Prom.counter (Prom.Info "pgrst_schema_cache_loads_total" "The total number of times the schema cache was loaded") schemaCacheLoads <- register $ vector "status" $ counter (Info "pgrst_schema_cache_loads_total" "The total number of times the schema cache was loaded")
schemaCacheQueryTime <- Prom.register $ Prom.gauge (Prom.Info "pgrst_schema_cache_query_time_seconds" "The query time in seconds of the last schema cache load") schemaCacheQueryTime <- register $ gauge (Info "pgrst_schema_cache_query_time_seconds" "The query time in seconds of the last schema cache load")
Prom.setGauge poolMaxSize (fromIntegral configDbPoolSize) setGauge poolMaxSize (fromIntegral configDbPoolSize)
pure $ MetricsState poolTimeouts poolAvailable poolWaiting poolMaxSize schemaCacheLoads schemaCacheQueryTime pure $ MetricsState poolTimeouts poolAvailable poolWaiting poolMaxSize schemaCacheLoads schemaCacheQueryTime
observationMetrics :: MetricsState -> ObservationHandler observationMetrics :: MetricsState -> ObservationHandler
observationMetrics (MetricsState poolTimeouts poolAvailable poolWaiting _ schemaCacheLoads schemaCacheQueryTime) obs = case obs of observationMetrics (MetricsState poolTimeouts poolAvailable poolWaiting _ schemaCacheLoads schemaCacheQueryTime) obs = case obs of
(PoolAcqTimeoutObs _) -> do (PoolAcqTimeoutObs _) -> do
Prom.incCounter poolTimeouts incCounter poolTimeouts
(HasqlPoolObs (SQL.ConnectionObservation _ status)) -> case status of (HasqlPoolObs (SQL.ConnectionObservation _ status)) -> case status of
SQL.ReadyForUseConnectionStatus -> do SQL.ReadyForUseConnectionStatus -> do
Prom.incGauge poolAvailable incGauge poolAvailable
SQL.InUseConnectionStatus -> do SQL.InUseConnectionStatus -> do
Prom.decGauge poolAvailable decGauge poolAvailable
SQL.TerminatedConnectionStatus _ -> do SQL.TerminatedConnectionStatus _ -> do
Prom.decGauge poolAvailable decGauge poolAvailable
SQL.ConnectingConnectionStatus -> pure () SQL.ConnectingConnectionStatus -> pure ()
PoolRequest -> PoolRequest ->
Prom.incGauge poolWaiting incGauge poolWaiting
PoolRequestFullfilled -> PoolRequestFullfilled ->
Prom.decGauge poolWaiting decGauge poolWaiting
SchemaCacheLoadedObs resTime -> do SchemaCacheLoadedObs resTime -> do
Prom.incCounter schemaCacheLoads withLabel schemaCacheLoads "SUCCESS" incCounter
Prom.setGauge schemaCacheQueryTime resTime setGauge schemaCacheQueryTime resTime
SchemaCacheNormalErrorObs _ -> do
withLabel schemaCacheLoads "FAIL" incCounter
_ -> _ ->
pure () pure ()
metricsToText :: IO LBS.ByteString metricsToText :: IO LBS.ByteString
metricsToText = Prom.exportMetricsAsText metricsToText = exportMetricsAsText
+39 -1
View File
@@ -732,6 +732,44 @@ def test_admin_ready_includes_schema_cache_state(defaultenv, metapostgrest):
reset_statement_timeout(metapostgrest, role) reset_statement_timeout(metapostgrest, role)
def test_metrics_include_schema_cache_fails(defaultenv, metapostgrest):
"Should get shema cache fails from the metrics endpoint"
role = "timeout_authenticator"
env = {
**defaultenv,
"PGUSER": role,
"PGRST_INTERNAL_SCHEMA_CACHE_SLEEP": "50",
}
with run(env=env) as postgrest:
# The schema cache query takes at least 20ms, due to PGRST_INTERNAL_SCHEMA_CACHE_SLEEP above.
# Make it impossible to load the schema cache, by setting statement timeout to 100ms.
set_statement_timeout(metapostgrest, role, 20)
# force a reconnection so the new role setting is picked up
postgrest.process.send_signal(signal.SIGUSR1)
# wait for some schema cache retries
time.sleep(1)
response = postgrest.admin.get("/ready", timeout=1)
assert response.status_code == 503
response = postgrest.admin.get("/metrics", timeout=1)
assert response.status_code == 200
metrics = float(
re.search(
r'pgrst_schema_cache_loads_total{status="FAIL"} (\d+)', response.text
).group(1)
)
assert metrics > 3.0
reset_statement_timeout(metapostgrest, role)
def test_admin_not_found(defaultenv): def test_admin_not_found(defaultenv):
"Should get a not found from a undefined endpoint on the admin server" "Should get a not found from a undefined endpoint on the admin server"
@@ -1448,7 +1486,7 @@ def test_admin_metrics(defaultenv):
response = postgrest.admin.get("/metrics") response = postgrest.admin.get("/metrics")
assert response.status_code == 200 assert response.status_code == 200
assert "pgrst_schema_cache_query_time_seconds" in response.text assert "pgrst_schema_cache_query_time_seconds" in response.text
assert "pgrst_schema_cache_loads_total" in response.text assert 'pgrst_schema_cache_loads_total{status="SUCCESS"}' in response.text
assert "pgrst_db_pool_max" in response.text assert "pgrst_db_pool_max" in response.text
assert "pgrst_db_pool_waiting" in response.text assert "pgrst_db_pool_waiting" in response.text
assert "pgrst_db_pool_available" in response.text assert "pgrst_db_pool_available" in response.text