@@ -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
@@ -17,12 +20,18 @@ import Prometheus
1720
1821import PostgREST.Observation
1922
20- import Protolude
23+ import Control.Arrow ((&&&) )
24+ import Data.Bitraversable (bisequenceA )
25+ import Data.Tuple.Extra (both )
26+ import Data.UUID (UUID )
27+ import qualified Focus
28+ import Protolude
29+ import qualified StmHamt.SizedHamt as SH
2130
2231data MetricsState =
2332 MetricsState {
2433 poolTimeouts :: Counter ,
25- poolAvailable :: Gauge ,
34+ connTrack :: ConnTrack ,
2635 poolWaiting :: Gauge ,
2736 poolMaxSize :: Gauge ,
2837 schemaCacheLoads :: Vector Label1 Counter ,
@@ -36,7 +45,7 @@ init :: Int -> IO MetricsState
3645init configDbPoolSize = do
3746 metricState <- MetricsState <$>
3847 register (counter (Info " pgrst_db_pool_timeouts_total" " The total number of pool connection timeouts" )) <*>
39- register (gauge ( Info " pgrst_db_pool_available " " Available connections in the pool " )) <*>
48+ register (Metric ((identity &&& dbPoolAvailable) <$> connectionTracker )) <*>
4049 register (gauge (Info " pgrst_db_pool_waiting" " Requests waiting to acquire a pool connection" )) <*>
4150 register (gauge (Info " pgrst_db_pool_max" " Max pool connections" )) <*>
4251 register (vector " status" $ counter (Info " pgrst_schema_cache_loads_total" " The total number of times the schema cache was loaded" )) <*>
@@ -46,20 +55,28 @@ init configDbPoolSize = do
4655 register (counter (Info " pgrst_jwt_cache_evictions_total" " The total number of JWT cache evictions" ))
4756 setGauge (poolMaxSize metricState) (fromIntegral configDbPoolSize)
4857 pure metricState
58+ where
59+ dbPoolAvailable = (pure . noLabelsGroup (Info " pgrst_db_pool_available" " Available connections in the pool" ) GaugeType . calcAvailable <$> ) . connectionCounts
60+ where
61+ calcAvailable = liftA2 (-) connected inUse
62+ toSample name labels = Sample name labels . encodeUtf8 . show
63+ noLabelsGroup info sampleType = SampleGroup info sampleType . pure . toSample (metricName info) mempty
4964
5065-- Only some observations are used as metrics
5166observationMetrics :: MetricsState -> ObservationHandler
5267observationMetrics MetricsState {.. } obs = case obs of
5368 PoolAcqTimeoutObs -> do
5469 incCounter poolTimeouts
55- (HasqlPoolObs (SQL. ConnectionObservation _ status)) -> case status of
56- SQL. ReadyForUseConnectionStatus _ -> do
57- incGauge poolAvailable
58- SQL. InUseConnectionStatus -> do
59- decGauge poolAvailable
60- SQL. TerminatedConnectionStatus _ -> do
61- decGauge poolAvailable
62- SQL. ConnectingConnectionStatus -> pure ()
70+ -- Handle pool observations with connection tracking
71+ -- this is necessary because it is not possible
72+ -- to accurately maintain open/in use conneciton counts
73+ -- statelessly based only on pool observation events.
74+ -- The reason is that hasql-pool emits TerminatedConnectionStatus
75+ -- both for connections successfully established and failed when connecting.
76+ -- When receiving TerminatedConnectionStatus we have to find out
77+ -- if we can decrement established connection count. To do that we have to track
78+ -- established connections.
79+ (HasqlPoolObs sqlObs) -> trackConnections connTrack sqlObs
6380 PoolRequest ->
6481 incGauge poolWaiting
6582 PoolRequestFullfilled ->
@@ -77,3 +94,28 @@ observationMetrics MetricsState{..} obs = case obs of
7794
7895metricsToText :: IO LBS. ByteString
7996metricsToText = exportMetricsAsText
97+
98+ data ConnStats = ConnStats {
99+ connected :: Int ,
100+ inUse :: Int
101+ } deriving (Eq , Show )
102+
103+ data ConnTrack = ConnTrack { connTrackConnected :: SH. SizedHamt UUID , connTrackInUse :: SH. SizedHamt UUID }
104+
105+ connectionTracker :: IO ConnTrack
106+ connectionTracker = ConnTrack <$> SH. newIO <*> SH. newIO
107+
108+ trackConnections :: ConnTrack -> SQL. Observation -> IO ()
109+ trackConnections ConnTrack {.. } (SQL. ConnectionObservation uuid status) = case status of
110+ SQL. ReadyForUseConnectionStatus _ -> atomically $
111+ SH. insert identity uuid connTrackConnected *>
112+ SH. focus Focus. delete identity uuid connTrackInUse
113+ SQL. TerminatedConnectionStatus _ -> atomically $
114+ SH. focus Focus. delete identity uuid connTrackConnected *>
115+ SH. focus Focus. delete identity uuid connTrackInUse
116+ SQL. InUseConnectionStatus -> atomically $
117+ SH. insert identity uuid connTrackInUse
118+ _ -> mempty
119+
120+ connectionCounts :: ConnTrack -> IO ConnStats
121+ connectionCounts = atomically . fmap (uncurry ConnStats ) . bisequenceA . both SH. size . (connTrackConnected &&& connTrackInUse)
0 commit comments