{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE OverloadedStrings #-}

-- | Postgres health snapshots from the stats and catalog views. The queries name Arbiter's
-- own tables and do not scan them.
module Arbiter.Core.Health
  ( PgDbHealth (..)
  , PgTableHealth (..)
  , getPgHealth
  , getPgDbHealth
  ) where

import Data.Aeson (FromJSON, ToJSON)
import Data.Int (Int64)
import Data.Text (Text)
import GHC.Generics (Generic)

import Arbiter.Core.Codec (Col (..), RowCodec, col, ncol)
import Arbiter.Core.Job.Schema (TableName)
import Arbiter.Core.MonadArbiter (MonadArbiter (..), queryMaybe)
import Arbiter.Core.SchemaTables (allSchemaTables)
import Arbiter.Core.Sql.Health qualified as Sql
import Arbiter.Core.Sql.Query (rows)

-- | Connection and age counters for the current database, shared with its other clients.
data PgDbHealth = PgDbHealth
  { PgDbHealth -> Int64
numBackends :: Int64
  -- ^ Connections to the database, the reading one excluded.
  , PgDbHealth -> Int64
connActive :: Int64
  -- ^ Connections running a query and not waiting on a lock.
  , PgDbHealth -> Int64
connIdle :: Int64
  -- ^ Idle connections.
  , PgDbHealth -> Int64
connIdleInTxn :: Int64
  -- ^ Connections idle inside an open transaction.
  , PgDbHealth -> Int64
connIdleInTxnAborted :: Int64
  -- ^ Connections idle inside a failed transaction.
  , PgDbHealth -> Int64
connBlocked :: Int64
  -- ^ Connections running a query that waits on a lock.
  , PgDbHealth -> Int64
connOther :: Int64
  -- ^ Connections in any other state, including ones whose state is hidden without
  -- @pg_read_all_stats@.
  , PgDbHealth -> Double
oldestTxnAge :: Double
  -- ^ Age of the oldest open transaction, in seconds. 0 when none is open.
  , PgDbHealth -> Double
oldestQueryAge :: Double
  -- ^ Age of the oldest running query, in seconds. 0 when none runs.
  }
  deriving stock (PgDbHealth -> PgDbHealth -> Bool
(PgDbHealth -> PgDbHealth -> Bool)
-> (PgDbHealth -> PgDbHealth -> Bool) -> Eq PgDbHealth
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: PgDbHealth -> PgDbHealth -> Bool
== :: PgDbHealth -> PgDbHealth -> Bool
$c/= :: PgDbHealth -> PgDbHealth -> Bool
/= :: PgDbHealth -> PgDbHealth -> Bool
Eq, (forall x. PgDbHealth -> Rep PgDbHealth x)
-> (forall x. Rep PgDbHealth x -> PgDbHealth) -> Generic PgDbHealth
forall x. Rep PgDbHealth x -> PgDbHealth
forall x. PgDbHealth -> Rep PgDbHealth x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. PgDbHealth -> Rep PgDbHealth x
from :: forall x. PgDbHealth -> Rep PgDbHealth x
$cto :: forall x. Rep PgDbHealth x -> PgDbHealth
to :: forall x. Rep PgDbHealth x -> PgDbHealth
Generic, Int -> PgDbHealth -> ShowS
[PgDbHealth] -> ShowS
PgDbHealth -> String
(Int -> PgDbHealth -> ShowS)
-> (PgDbHealth -> String)
-> ([PgDbHealth] -> ShowS)
-> Show PgDbHealth
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PgDbHealth -> ShowS
showsPrec :: Int -> PgDbHealth -> ShowS
$cshow :: PgDbHealth -> String
show :: PgDbHealth -> String
$cshowList :: [PgDbHealth] -> ShowS
showList :: [PgDbHealth] -> ShowS
Show)
  deriving anyclass (Maybe PgDbHealth
Value -> Parser [PgDbHealth]
Value -> Parser PgDbHealth
(Value -> Parser PgDbHealth)
-> (Value -> Parser [PgDbHealth])
-> Maybe PgDbHealth
-> FromJSON PgDbHealth
forall a.
(Value -> Parser a)
-> (Value -> Parser [a]) -> Maybe a -> FromJSON a
$cparseJSON :: Value -> Parser PgDbHealth
parseJSON :: Value -> Parser PgDbHealth
$cparseJSONList :: Value -> Parser [PgDbHealth]
parseJSONList :: Value -> Parser [PgDbHealth]
$comittedField :: Maybe PgDbHealth
omittedField :: Maybe PgDbHealth
FromJSON, [PgDbHealth] -> Value
[PgDbHealth] -> Encoding
PgDbHealth -> Bool
PgDbHealth -> Value
PgDbHealth -> Encoding
(PgDbHealth -> Value)
-> (PgDbHealth -> Encoding)
-> ([PgDbHealth] -> Value)
-> ([PgDbHealth] -> Encoding)
-> (PgDbHealth -> Bool)
-> ToJSON PgDbHealth
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> (a -> Bool)
-> ToJSON a
$ctoJSON :: PgDbHealth -> Value
toJSON :: PgDbHealth -> Value
$ctoEncoding :: PgDbHealth -> Encoding
toEncoding :: PgDbHealth -> Encoding
$ctoJSONList :: [PgDbHealth] -> Value
toJSONList :: [PgDbHealth] -> Value
$ctoEncodingList :: [PgDbHealth] -> Encoding
toEncodingList :: [PgDbHealth] -> Encoding
$comitField :: PgDbHealth -> Bool
omitField :: PgDbHealth -> Bool
ToJSON)

-- | Per-table tuple counts, size, scan counters, block traffic, and freeze age.
data PgTableHealth = PgTableHealth
  { PgTableHealth -> Text
table :: Text
  , PgTableHealth -> Int64
liveTup :: Int64
  -- ^ Estimated live rows.
  , PgTableHealth -> Int64
deadTup :: Int64
  -- ^ Estimated dead rows.
  , PgTableHealth -> Maybe Double
vacuumAge :: Maybe Double
  -- ^ Seconds since the last vacuum, manual or auto. 'Nothing' when the table has never been vacuumed.
  , PgTableHealth -> Int64
totalBytes :: Int64
  -- ^ Size with indexes and TOAST, in bytes.
  , PgTableHealth -> Double
seqScan :: Double
  -- ^ Sequential scans started.
  , PgTableHealth -> Double
idxScan :: Double
  -- ^ Index scans started.
  , PgTableHealth -> Double
blksHit :: Double
  -- ^ Blocks read from the buffer cache, for the table, its indexes, and its TOAST.
  , PgTableHealth -> Double
blksRead :: Double
  -- ^ Blocks read from disk, for the table, its indexes, and its TOAST.
  , PgTableHealth -> Maybe Int64
xidAge :: Maybe Int64
  -- ^ Age of the table's frozen transaction id, in transactions. 'Nothing' when the table has no frozen transaction id.
  }
  deriving stock (PgTableHealth -> PgTableHealth -> Bool
(PgTableHealth -> PgTableHealth -> Bool)
-> (PgTableHealth -> PgTableHealth -> Bool) -> Eq PgTableHealth
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: PgTableHealth -> PgTableHealth -> Bool
== :: PgTableHealth -> PgTableHealth -> Bool
$c/= :: PgTableHealth -> PgTableHealth -> Bool
/= :: PgTableHealth -> PgTableHealth -> Bool
Eq, (forall x. PgTableHealth -> Rep PgTableHealth x)
-> (forall x. Rep PgTableHealth x -> PgTableHealth)
-> Generic PgTableHealth
forall x. Rep PgTableHealth x -> PgTableHealth
forall x. PgTableHealth -> Rep PgTableHealth x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. PgTableHealth -> Rep PgTableHealth x
from :: forall x. PgTableHealth -> Rep PgTableHealth x
$cto :: forall x. Rep PgTableHealth x -> PgTableHealth
to :: forall x. Rep PgTableHealth x -> PgTableHealth
Generic, Int -> PgTableHealth -> ShowS
[PgTableHealth] -> ShowS
PgTableHealth -> String
(Int -> PgTableHealth -> ShowS)
-> (PgTableHealth -> String)
-> ([PgTableHealth] -> ShowS)
-> Show PgTableHealth
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PgTableHealth -> ShowS
showsPrec :: Int -> PgTableHealth -> ShowS
$cshow :: PgTableHealth -> String
show :: PgTableHealth -> String
$cshowList :: [PgTableHealth] -> ShowS
showList :: [PgTableHealth] -> ShowS
Show)
  deriving anyclass (Maybe PgTableHealth
Value -> Parser [PgTableHealth]
Value -> Parser PgTableHealth
(Value -> Parser PgTableHealth)
-> (Value -> Parser [PgTableHealth])
-> Maybe PgTableHealth
-> FromJSON PgTableHealth
forall a.
(Value -> Parser a)
-> (Value -> Parser [a]) -> Maybe a -> FromJSON a
$cparseJSON :: Value -> Parser PgTableHealth
parseJSON :: Value -> Parser PgTableHealth
$cparseJSONList :: Value -> Parser [PgTableHealth]
parseJSONList :: Value -> Parser [PgTableHealth]
$comittedField :: Maybe PgTableHealth
omittedField :: Maybe PgTableHealth
FromJSON, [PgTableHealth] -> Value
[PgTableHealth] -> Encoding
PgTableHealth -> Bool
PgTableHealth -> Value
PgTableHealth -> Encoding
(PgTableHealth -> Value)
-> (PgTableHealth -> Encoding)
-> ([PgTableHealth] -> Value)
-> ([PgTableHealth] -> Encoding)
-> (PgTableHealth -> Bool)
-> ToJSON PgTableHealth
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> (a -> Bool)
-> ToJSON a
$ctoJSON :: PgTableHealth -> Value
toJSON :: PgTableHealth -> Value
$ctoEncoding :: PgTableHealth -> Encoding
toEncoding :: PgTableHealth -> Encoding
$ctoJSONList :: [PgTableHealth] -> Value
toJSONList :: [PgTableHealth] -> Value
$ctoEncodingList :: [PgTableHealth] -> Encoding
toEncodingList :: [PgTableHealth] -> Encoding
$comitField :: PgTableHealth -> Bool
omitField :: PgTableHealth -> Bool
ToJSON)

-- | Column order matches the SELECT lists. 'RowCodec' is positional.
pgDbHealthCodec :: RowCodec PgDbHealth
pgDbHealthCodec :: RowCodec PgDbHealth
pgDbHealthCodec =
  Int64
-> Int64
-> Int64
-> Int64
-> Int64
-> Int64
-> Int64
-> Double
-> Double
-> PgDbHealth
PgDbHealth
    (Int64
 -> Int64
 -> Int64
 -> Int64
 -> Int64
 -> Int64
 -> Int64
 -> Double
 -> Double
 -> PgDbHealth)
-> Ap NullCol Int64
-> Ap
     NullCol
     (Int64
      -> Int64
      -> Int64
      -> Int64
      -> Int64
      -> Int64
      -> Double
      -> Double
      -> PgDbHealth)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Col Int64 -> Ap NullCol Int64
forall a. Text -> Col a -> RowCodec a
col Text
"numbackends" Col Int64
CInt8
    Ap
  NullCol
  (Int64
   -> Int64
   -> Int64
   -> Int64
   -> Int64
   -> Int64
   -> Double
   -> Double
   -> PgDbHealth)
-> Ap NullCol Int64
-> Ap
     NullCol
     (Int64
      -> Int64
      -> Int64
      -> Int64
      -> Int64
      -> Double
      -> Double
      -> PgDbHealth)
forall a b. Ap NullCol (a -> b) -> Ap NullCol a -> Ap NullCol b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Col Int64 -> Ap NullCol Int64
forall a. Text -> Col a -> RowCodec a
col Text
"active" Col Int64
CInt8
    Ap
  NullCol
  (Int64
   -> Int64
   -> Int64
   -> Int64
   -> Int64
   -> Double
   -> Double
   -> PgDbHealth)
-> Ap NullCol Int64
-> Ap
     NullCol
     (Int64
      -> Int64 -> Int64 -> Int64 -> Double -> Double -> PgDbHealth)
forall a b. Ap NullCol (a -> b) -> Ap NullCol a -> Ap NullCol b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Col Int64 -> Ap NullCol Int64
forall a. Text -> Col a -> RowCodec a
col Text
"idle" Col Int64
CInt8
    Ap
  NullCol
  (Int64
   -> Int64 -> Int64 -> Int64 -> Double -> Double -> PgDbHealth)
-> Ap NullCol Int64
-> Ap
     NullCol (Int64 -> Int64 -> Int64 -> Double -> Double -> PgDbHealth)
forall a b. Ap NullCol (a -> b) -> Ap NullCol a -> Ap NullCol b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Col Int64 -> Ap NullCol Int64
forall a. Text -> Col a -> RowCodec a
col Text
"idle_in_txn" Col Int64
CInt8
    Ap
  NullCol (Int64 -> Int64 -> Int64 -> Double -> Double -> PgDbHealth)
-> Ap NullCol Int64
-> Ap NullCol (Int64 -> Int64 -> Double -> Double -> PgDbHealth)
forall a b. Ap NullCol (a -> b) -> Ap NullCol a -> Ap NullCol b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Col Int64 -> Ap NullCol Int64
forall a. Text -> Col a -> RowCodec a
col Text
"idle_in_txn_aborted" Col Int64
CInt8
    Ap NullCol (Int64 -> Int64 -> Double -> Double -> PgDbHealth)
-> Ap NullCol Int64
-> Ap NullCol (Int64 -> Double -> Double -> PgDbHealth)
forall a b. Ap NullCol (a -> b) -> Ap NullCol a -> Ap NullCol b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Col Int64 -> Ap NullCol Int64
forall a. Text -> Col a -> RowCodec a
col Text
"blocked" Col Int64
CInt8
    Ap NullCol (Int64 -> Double -> Double -> PgDbHealth)
-> Ap NullCol Int64 -> Ap NullCol (Double -> Double -> PgDbHealth)
forall a b. Ap NullCol (a -> b) -> Ap NullCol a -> Ap NullCol b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Col Int64 -> Ap NullCol Int64
forall a. Text -> Col a -> RowCodec a
col Text
"other" Col Int64
CInt8
    Ap NullCol (Double -> Double -> PgDbHealth)
-> Ap NullCol Double -> Ap NullCol (Double -> PgDbHealth)
forall a b. Ap NullCol (a -> b) -> Ap NullCol a -> Ap NullCol b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Col Double -> Ap NullCol Double
forall a. Text -> Col a -> RowCodec a
col Text
"oldest_txn_age" Col Double
CFloat8
    Ap NullCol (Double -> PgDbHealth)
-> Ap NullCol Double -> RowCodec PgDbHealth
forall a b. Ap NullCol (a -> b) -> Ap NullCol a -> Ap NullCol b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Col Double -> Ap NullCol Double
forall a. Text -> Col a -> RowCodec a
col Text
"oldest_query_age" Col Double
CFloat8

pgTableHealthCodec :: RowCodec PgTableHealth
pgTableHealthCodec :: RowCodec PgTableHealth
pgTableHealthCodec =
  Text
-> Int64
-> Int64
-> Maybe Double
-> Int64
-> Double
-> Double
-> Double
-> Double
-> Maybe Int64
-> PgTableHealth
PgTableHealth
    (Text
 -> Int64
 -> Int64
 -> Maybe Double
 -> Int64
 -> Double
 -> Double
 -> Double
 -> Double
 -> Maybe Int64
 -> PgTableHealth)
-> Ap NullCol Text
-> Ap
     NullCol
     (Int64
      -> Int64
      -> Maybe Double
      -> Int64
      -> Double
      -> Double
      -> Double
      -> Double
      -> Maybe Int64
      -> PgTableHealth)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Col Text -> Ap NullCol Text
forall a. Text -> Col a -> RowCodec a
col Text
"relname" Col Text
CText
    Ap
  NullCol
  (Int64
   -> Int64
   -> Maybe Double
   -> Int64
   -> Double
   -> Double
   -> Double
   -> Double
   -> Maybe Int64
   -> PgTableHealth)
-> Ap NullCol Int64
-> Ap
     NullCol
     (Int64
      -> Maybe Double
      -> Int64
      -> Double
      -> Double
      -> Double
      -> Double
      -> Maybe Int64
      -> PgTableHealth)
forall a b. Ap NullCol (a -> b) -> Ap NullCol a -> Ap NullCol b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Col Int64 -> Ap NullCol Int64
forall a. Text -> Col a -> RowCodec a
col Text
"n_live_tup" Col Int64
CInt8
    Ap
  NullCol
  (Int64
   -> Maybe Double
   -> Int64
   -> Double
   -> Double
   -> Double
   -> Double
   -> Maybe Int64
   -> PgTableHealth)
-> Ap NullCol Int64
-> Ap
     NullCol
     (Maybe Double
      -> Int64
      -> Double
      -> Double
      -> Double
      -> Double
      -> Maybe Int64
      -> PgTableHealth)
forall a b. Ap NullCol (a -> b) -> Ap NullCol a -> Ap NullCol b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Col Int64 -> Ap NullCol Int64
forall a. Text -> Col a -> RowCodec a
col Text
"n_dead_tup" Col Int64
CInt8
    Ap
  NullCol
  (Maybe Double
   -> Int64
   -> Double
   -> Double
   -> Double
   -> Double
   -> Maybe Int64
   -> PgTableHealth)
-> Ap NullCol (Maybe Double)
-> Ap
     NullCol
     (Int64
      -> Double
      -> Double
      -> Double
      -> Double
      -> Maybe Int64
      -> PgTableHealth)
forall a b. Ap NullCol (a -> b) -> Ap NullCol a -> Ap NullCol b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Col Double -> Ap NullCol (Maybe Double)
forall a. Text -> Col a -> RowCodec (Maybe a)
ncol Text
"vacuum_age" Col Double
CFloat8
    Ap
  NullCol
  (Int64
   -> Double
   -> Double
   -> Double
   -> Double
   -> Maybe Int64
   -> PgTableHealth)
-> Ap NullCol Int64
-> Ap
     NullCol
     (Double
      -> Double -> Double -> Double -> Maybe Int64 -> PgTableHealth)
forall a b. Ap NullCol (a -> b) -> Ap NullCol a -> Ap NullCol b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Col Int64 -> Ap NullCol Int64
forall a. Text -> Col a -> RowCodec a
col Text
"total_bytes" Col Int64
CInt8
    Ap
  NullCol
  (Double
   -> Double -> Double -> Double -> Maybe Int64 -> PgTableHealth)
-> Ap NullCol Double
-> Ap
     NullCol
     (Double -> Double -> Double -> Maybe Int64 -> PgTableHealth)
forall a b. Ap NullCol (a -> b) -> Ap NullCol a -> Ap NullCol b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Col Double -> Ap NullCol Double
forall a. Text -> Col a -> RowCodec a
col Text
"seq_scan" Col Double
CFloat8
    Ap
  NullCol
  (Double -> Double -> Double -> Maybe Int64 -> PgTableHealth)
-> Ap NullCol Double
-> Ap NullCol (Double -> Double -> Maybe Int64 -> PgTableHealth)
forall a b. Ap NullCol (a -> b) -> Ap NullCol a -> Ap NullCol b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Col Double -> Ap NullCol Double
forall a. Text -> Col a -> RowCodec a
col Text
"idx_scan" Col Double
CFloat8
    Ap NullCol (Double -> Double -> Maybe Int64 -> PgTableHealth)
-> Ap NullCol Double
-> Ap NullCol (Double -> Maybe Int64 -> PgTableHealth)
forall a b. Ap NullCol (a -> b) -> Ap NullCol a -> Ap NullCol b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Col Double -> Ap NullCol Double
forall a. Text -> Col a -> RowCodec a
col Text
"blks_hit" Col Double
CFloat8
    Ap NullCol (Double -> Maybe Int64 -> PgTableHealth)
-> Ap NullCol Double -> Ap NullCol (Maybe Int64 -> PgTableHealth)
forall a b. Ap NullCol (a -> b) -> Ap NullCol a -> Ap NullCol b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Col Double -> Ap NullCol Double
forall a. Text -> Col a -> RowCodec a
col Text
"blks_read" Col Double
CFloat8
    Ap NullCol (Maybe Int64 -> PgTableHealth)
-> Ap NullCol (Maybe Int64) -> RowCodec PgTableHealth
forall a b. Ap NullCol (a -> b) -> Ap NullCol a -> Ap NullCol b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Col Int64 -> Ap NullCol (Maybe Int64)
forall a. Text -> Col a -> RowCodec (Maybe a)
ncol Text
"xid_age" Col Int64
CInt8

-- | Database-wide health and per-table churn for the given queues' tables and the schema's
-- shared arbiter tables. Backends owned by another role report their state as unknown,
-- absent @pg_read_all_stats@.
getPgHealth :: (MonadArbiter m) => [TableName] -> m (Maybe PgDbHealth, [PgTableHealth])
getPgHealth :: forall (m :: * -> *).
MonadArbiter m =>
[Text] -> m (Maybe PgDbHealth, [PgTableHealth])
getPgHealth [Text]
queueTables = do
  schemaName <- m Text
forall (m :: * -> *). MonadArbiter m => m Text
getSchema
  dbHealth <- getPgDbHealth
  tableRows <- executeQuery (rows pgTableHealthCodec (Sql.pgTableHealthSQL schemaName scanned))
  pure (dbHealth, tableRows)
  where
    scanned :: [Text]
scanned = [Text] -> [Text]
allSchemaTables [Text]
queueTables

-- | The database-wide half on its own.
getPgDbHealth :: (MonadArbiter m) => m (Maybe PgDbHealth)
getPgDbHealth :: forall (m :: * -> *). MonadArbiter m => m (Maybe PgDbHealth)
getPgDbHealth = Query PgDbHealth -> m (Maybe PgDbHealth)
forall (m :: * -> *) a. MonadArbiter m => Query a -> m (Maybe a)
queryMaybe (RowCodec PgDbHealth -> Query () -> Query PgDbHealth
forall a. RowCodec a -> Query () -> Query a
rows RowCodec PgDbHealth
pgDbHealthCodec Query ()
Sql.pgDbHealthSQL)