diff --git a/CHANGELOG.md b/CHANGELOG.md index 37136c6c6..72394c45e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 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 +- Add GHC runtime metrics to the metrics endpoint by @mkleczek in #4862 ### Fixed diff --git a/docs/postgrest.dict b/docs/postgrest.dict index 355c72dcc..324d51b88 100644 --- a/docs/postgrest.dict +++ b/docs/postgrest.dict @@ -45,7 +45,9 @@ filename FreeBSD fts fullstack +GC GeoJSON +GHC Github Google grantor @@ -147,6 +149,7 @@ RESTful RLS RPC RSA +RTS safeupdate savepoint schemas @@ -200,4 +203,4 @@ webuser wfts www debouncing -deduplicates \ No newline at end of file +deduplicates diff --git a/docs/references/observability.rst b/docs/references/observability.rst index 2a06598db..0c40d7ff5 100644 --- a/docs/references/observability.rst +++ b/docs/references/observability.rst @@ -238,6 +238,45 @@ pgrst_jwt_cache_evictions_total 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 `_ +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 ====== diff --git a/postgrest.cabal b/postgrest.cabal index 2a819d80b..2f100b3e9 100644 --- a/postgrest.cabal +++ b/postgrest.cabal @@ -136,6 +136,7 @@ library , parsec >= 3.1.11 && < 3.2 , postgresql-libpq >= 0.10 , prometheus-client >= 1.1.1 && < 1.2.0 + , prometheus-metrics-ghc >= 1.0.1.2 && < 1.2 , protolude >= 0.3.1 && < 0.4 , regex-tdfa >= 1.2.2 && < 1.4 , retry >= 0.7.4 && < 0.10 diff --git a/src/PostgREST/Metrics.hs b/src/PostgREST/Metrics.hs index bf7ce017b..75db34b4d 100644 --- a/src/PostgREST/Metrics.hs +++ b/src/PostgREST/Metrics.hs @@ -13,10 +13,13 @@ module PostgREST.Metrics import qualified Data.ByteString.Lazy as LBS 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 Protolude data MetricsState = @@ -34,6 +37,7 @@ data MetricsState = init :: Int -> IO MetricsState init configDbPoolSize = do + whenM getRTSStatsEnabled $ void $ register PMG.ghcMetrics metricState <- MetricsState <$> 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")) <*> diff --git a/test/io/postgrest.py b/test/io/postgrest.py index 33a64a0d9..8e23387e1 100644 --- a/test/io/postgrest.py +++ b/test/io/postgrest.py @@ -84,6 +84,7 @@ def run( configpath=None, stdin=None, env=None, + args=None, port=None, admin_port=None, host=None, @@ -122,6 +123,9 @@ def run( command = [POSTGREST_BIN] env["HPCTIXFILE"] = hpctixfile() + if args: + command.extend(args) + if configpath: command.append(configpath) diff --git a/test/io/test_io.py b/test/io/test_io.py index c59721ebc..f2e255bca 100644 --- a/test/io/test_io.py +++ b/test/io/test_io.py @@ -1693,6 +1693,30 @@ def test_admin_metrics(defaultenv): 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): "verify that the Schema Cache loads correctly at startup, using the in-db `pgrst.db_schemas` config"