@@ -5,7 +5,10 @@ Description : Metrics based on the Observation module. See Observation.hs.
55-}
66module PostgREST.Metrics
77 ( init
8+ , ConnTrack
9+ , ConnStats (.. )
810 , MetricsState (.. )
11+ , connectionCounts
912 , observationMetrics
1013 , metricsToText
1114 ) where
@@ -19,13 +22,18 @@ import qualified Prometheus.Metric.GHC as PMG
1922
2023import PostgREST.Observation
2124
22-
23- import Protolude
25+ import Control.Arrow ((&&&) )
26+ import Data.Bitraversable (bisequenceA )
27+ import Data.Tuple.Extra (both )
28+ import Data.UUID (UUID )
29+ import qualified Focus
30+ import Protolude
31+ import qualified StmHamt.SizedHamt as SH
2432
2533data MetricsState =
2634 MetricsState {
2735 poolTimeouts :: Counter ,
28- poolAvailable :: Gauge ,
36+ connTrack :: ConnTrack ,
2937 poolWaiting :: Gauge ,
3038 poolMaxSize :: Gauge ,
3139 schemaCacheLoads :: Vector Label1 Counter ,
@@ -40,7 +48,7 @@ init configDbPoolSize = do
4048 whenM getRTSStatsEnabled $ void $ register PMG. ghcMetrics
4149 metricState <- MetricsState <$>
4250 register (counter (Info " pgrst_db_pool_timeouts_total" " The total number of pool connection timeouts" )) <*>
43- register (gauge ( Info " pgrst_db_pool_available " " Available connections in the pool " )) <*>
51+ register (Metric ((identity &&& dbPoolAvailable) <$> connectionTracker )) <*>
4452 register (gauge (Info " pgrst_db_pool_waiting" " Requests waiting to acquire a pool connection" )) <*>
4553 register (gauge (Info " pgrst_db_pool_max" " Max pool connections" )) <*>
4654 register (vector " status" $ counter (Info " pgrst_schema_cache_loads_total" " The total number of times the schema cache was loaded" )) <*>
@@ -50,20 +58,28 @@ init configDbPoolSize = do
5058 register (counter (Info " pgrst_jwt_cache_evictions_total" " The total number of JWT cache evictions" ))
5159 setGauge (poolMaxSize metricState) (fromIntegral configDbPoolSize)
5260 pure metricState
61+ where
62+ dbPoolAvailable = (pure . noLabelsGroup (Info " pgrst_db_pool_available" " Available connections in the pool" ) GaugeType . calcAvailable <$> ) . connectionCounts
63+ where
64+ calcAvailable = liftA2 (-) connected inUse
65+ toSample name labels = Sample name labels . encodeUtf8 . show
66+ noLabelsGroup info sampleType = SampleGroup info sampleType . pure . toSample (metricName info) mempty
5367
5468-- Only some observations are used as metrics
5569observationMetrics :: MetricsState -> ObservationHandler
5670observationMetrics MetricsState {.. } obs = case obs of
5771 PoolAcqTimeoutObs -> do
5872 incCounter poolTimeouts
59- (HasqlPoolObs (SQL. ConnectionObservation _ status)) -> case status of
60- SQL. ReadyForUseConnectionStatus _ -> do
61- incGauge poolAvailable
62- SQL. InUseConnectionStatus -> do
63- decGauge poolAvailable
64- SQL. TerminatedConnectionStatus _ -> do
65- decGauge poolAvailable
66- SQL. ConnectingConnectionStatus -> pure ()
73+ -- Handle pool observations with connection tracking
74+ -- this is necessary because it is not possible
75+ -- to accurately maintain open/in use conneciton counts
76+ -- statelessly based only on pool observation events.
77+ -- The reason is that hasql-pool emits TerminatedConnectionStatus
78+ -- both for connections successfully established and failed when connecting.
79+ -- When receiving TerminatedConnectionStatus we have to find out
80+ -- if we can decrement established connection count. To do that we have to track
81+ -- established connections.
82+ (HasqlPoolObs sqlObs) -> trackConnections connTrack sqlObs
6783 PoolRequest ->
6884 incGauge poolWaiting
6985 PoolRequestFullfilled ->
@@ -81,3 +97,28 @@ observationMetrics MetricsState{..} obs = case obs of
8197
8298metricsToText :: IO LBS. ByteString
8399metricsToText = exportMetricsAsText
100+
101+ data ConnStats = ConnStats {
102+ connected :: Int ,
103+ inUse :: Int
104+ } deriving (Eq , Show )
105+
106+ data ConnTrack = ConnTrack { connTrackConnected :: SH. SizedHamt UUID , connTrackInUse :: SH. SizedHamt UUID }
107+
108+ connectionTracker :: IO ConnTrack
109+ connectionTracker = ConnTrack <$> SH. newIO <*> SH. newIO
110+
111+ trackConnections :: ConnTrack -> SQL. Observation -> IO ()
112+ trackConnections ConnTrack {.. } (SQL. ConnectionObservation uuid status) = case status of
113+ SQL. ReadyForUseConnectionStatus _ -> atomically $
114+ SH. insert identity uuid connTrackConnected *>
115+ SH. focus Focus. delete identity uuid connTrackInUse
116+ SQL. TerminatedConnectionStatus _ -> atomically $
117+ SH. focus Focus. delete identity uuid connTrackConnected *>
118+ SH. focus Focus. delete identity uuid connTrackInUse
119+ SQL. InUseConnectionStatus -> atomically $
120+ SH. insert identity uuid connTrackInUse
121+ _ -> mempty
122+
123+ connectionCounts :: ConnTrack -> IO ConnStats
124+ connectionCounts = atomically . fmap (uncurry ConnStats ) . bisequenceA . both SH. size . (connTrackConnected &&& connTrackInUse)
0 commit comments