add: Publish GHC runtime metrics to Prometheus metrics endpoint

This commit is contained in:
Michał Kłeczek
2026-05-03 11:41:02 +00:00
committed by Wolfgang Walther
parent c09394517d
commit 85d91e5c0a
7 changed files with 78 additions and 2 deletions
+1
View File
@@ -14,6 +14,7 @@ All notable changes to this project will be documented in this file. From versio
- Add `Vary` header to responses by @develop7 in #4609 - Add `Vary` header to responses by @develop7 in #4609
- Add config `db-timezone-enabled` for optional querying of timezones by @taimoorzaeem in #4751 - Add config `db-timezone-enabled` for optional querying of timezones by @taimoorzaeem in #4751
- Log schema cache queries timings on `log-level=debug` by @steve-chavez in #4805 - Log schema cache queries timings on `log-level=debug` by @steve-chavez in #4805
- Add GHC runtime metrics to the metrics endpoint by @mkleczek in #4862
### Fixed ### Fixed
+4 -1
View File
@@ -45,7 +45,9 @@ filename
FreeBSD FreeBSD
fts fts
fullstack fullstack
GC
GeoJSON GeoJSON
GHC
Github Github
Google Google
grantor grantor
@@ -147,6 +149,7 @@ RESTful
RLS RLS
RPC RPC
RSA RSA
RTS
safeupdate safeupdate
savepoint savepoint
schemas schemas
@@ -200,4 +203,4 @@ webuser
wfts wfts
www www
debouncing debouncing
deduplicates deduplicates
+39
View File
@@ -238,6 +238,45 @@ pgrst_jwt_cache_evictions_total
The total number of JWT cache evictions. The total number of JWT cache evictions.
GHC Runtime Metrics
-------------------
PostgREST can also expose GHC runtime system metrics. These use the ``ghc_*``
prefix and include
`GHC RTS statistics <https://ghc.gitlab.haskell.org/ghc/doc/libraries/base-4.22.0.0-inplace/GHC-Stats.html#g:1>`_
for runtime allocation, garbage collection, memory, and CPU/elapsed time.
These are useful for monitoring PostgREST process health and diagnosing memory
pressure or GC behavior.
To expose these metrics, enable GHC RTS statistics when starting PostgREST:
.. code-block:: bash
postgrest +RTS -T -RTS
When enabled, the admin ``/metrics`` endpoint includes samples such as:
.. code-block:: text
# HELP ghc_gcs_total Total number of GCs
# TYPE ghc_gcs_total counter
ghc_gcs_total 1
# HELP ghc_allocated_bytes_total Total bytes allocated
# TYPE ghc_allocated_bytes_total counter
ghc_allocated_bytes_total 12345678
Other available GHC runtime metrics include:
- ``ghc_gcs_total``
- ``ghc_major_gcs_total``
- ``ghc_allocated_bytes_total``
- ``ghc_max_live_bytes``
- ``ghc_max_mem_in_use_bytes``
- ``ghc_mutator_cpu_seconds_total``
- ``ghc_gc_cpu_seconds_total``
- ``ghc_elapsed_seconds_total``
Traces Traces
====== ======
+1
View File
@@ -136,6 +136,7 @@ library
, parsec >= 3.1.11 && < 3.2 , parsec >= 3.1.11 && < 3.2
, postgresql-libpq >= 0.10 , postgresql-libpq >= 0.10
, prometheus-client >= 1.1.1 && < 1.2.0 , prometheus-client >= 1.1.1 && < 1.2.0
, prometheus-metrics-ghc >= 1.0.1.2 && < 1.2
, protolude >= 0.3.1 && < 0.4 , protolude >= 0.3.1 && < 0.4
, regex-tdfa >= 1.2.2 && < 1.4 , regex-tdfa >= 1.2.2 && < 1.4
, retry >= 0.7.4 && < 0.10 , retry >= 0.7.4 && < 0.10
+5 -1
View File
@@ -13,10 +13,13 @@ 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 Prometheus import GHC.Stats (getRTSStatsEnabled)
import Prometheus
import qualified Prometheus.Metric.GHC as PMG
import PostgREST.Observation import PostgREST.Observation
import Protolude import Protolude
data MetricsState = data MetricsState =
@@ -34,6 +37,7 @@ data MetricsState =
init :: Int -> IO MetricsState init :: Int -> IO MetricsState
init configDbPoolSize = do init configDbPoolSize = do
whenM getRTSStatsEnabled $ void $ register PMG.ghcMetrics
metricState <- MetricsState <$> metricState <- MetricsState <$>
register (counter (Info "pgrst_db_pool_timeouts_total" "The total number of pool connection timeouts")) <*> register (counter (Info "pgrst_db_pool_timeouts_total" "The total number of pool connection timeouts")) <*>
register (gauge (Info "pgrst_db_pool_available" "Available connections in the pool")) <*> register (gauge (Info "pgrst_db_pool_available" "Available connections in the pool")) <*>
+4
View File
@@ -84,6 +84,7 @@ def run(
configpath=None, configpath=None,
stdin=None, stdin=None,
env=None, env=None,
args=None,
port=None, port=None,
admin_port=None, admin_port=None,
host=None, host=None,
@@ -122,6 +123,9 @@ def run(
command = [POSTGREST_BIN] command = [POSTGREST_BIN]
env["HPCTIXFILE"] = hpctixfile() env["HPCTIXFILE"] = hpctixfile()
if args:
command.extend(args)
if configpath: if configpath:
command.append(configpath) command.append(configpath)
+24
View File
@@ -1693,6 +1693,30 @@ def test_admin_metrics(defaultenv):
assert "pgrst_db_pool_timeouts_total" in response.text assert "pgrst_db_pool_timeouts_total" in response.text
def test_admin_metrics_include_ghc_runtime_metrics(defaultenv):
"Should get GHC runtime metrics from the admin endpoint when RTS stats are enabled"
with run(env=defaultenv, args=["+RTS", "-T", "-RTS"], port=freeport()) as postgrest:
response = postgrest.admin.get("/metrics")
assert response.status_code == 200
assert "# HELP ghc_gcs_total Total number of GCs" in response.text
assert "# TYPE ghc_gcs_total counter" in response.text
assert re.search(r"^ghc_gcs_total \d+(?:\.\d+)?$", response.text, re.MULTILINE)
assert re.search(
r"^ghc_allocated_bytes_total \d+(?:\.\d+)?$", response.text, re.MULTILINE
)
def test_admin_metrics_exclude_ghc_runtime_metrics_by_default(defaultenv):
"Should not get GHC runtime metrics unless RTS stats are enabled"
with run(env=defaultenv, port=freeport()) as postgrest:
response = postgrest.admin.get("/metrics")
assert response.status_code == 200
assert "ghc_gcs_total" not in response.text
assert "ghc_allocated_bytes_total" not in response.text
def test_schema_cache_startup_load_with_in_db_config(defaultenv, metapostgrest): def test_schema_cache_startup_load_with_in_db_config(defaultenv, metapostgrest):
"verify that the Schema Cache loads correctly at startup, using the in-db `pgrst.db_schemas` config" "verify that the Schema Cache loads correctly at startup, using the in-db `pgrst.db_schemas` config"