LCOV - code coverage report
Current view: top level - compute_tools/src - installed_extensions.rs (source / functions) Coverage Total Hit
Test: bb45db3982713bfd5bec075773079136e362195e.info Lines: 0.0 % 72 0
Test Date: 2024-12-11 15:53:32 Functions: 0.0 % 7 0

            Line data    Source code
       1              : use compute_api::responses::{InstalledExtension, InstalledExtensions};
       2              : use metrics::proto::MetricFamily;
       3              : use std::collections::HashMap;
       4              : use std::collections::HashSet;
       5              : 
       6              : use anyhow::Result;
       7              : use postgres::{Client, NoTls};
       8              : 
       9              : use metrics::core::Collector;
      10              : use metrics::{register_uint_gauge_vec, UIntGaugeVec};
      11              : use once_cell::sync::Lazy;
      12              : 
      13              : /// We don't reuse get_existing_dbs() just for code clarity
      14              : /// and to make database listing query here more explicit.
      15              : ///
      16              : /// Limit the number of databases to 500 to avoid excessive load.
      17            0 : fn list_dbs(client: &mut Client) -> Result<Vec<String>> {
      18              :     // `pg_database.datconnlimit = -2` means that the database is in the
      19              :     // invalid state
      20            0 :     let databases = client
      21            0 :         .query(
      22            0 :             "SELECT datname FROM pg_catalog.pg_database
      23            0 :                 WHERE datallowconn
      24            0 :                 AND datconnlimit <> - 2
      25            0 :                 LIMIT 500",
      26            0 :             &[],
      27            0 :         )?
      28            0 :         .iter()
      29            0 :         .map(|row| {
      30            0 :             let db: String = row.get("datname");
      31            0 :             db
      32            0 :         })
      33            0 :         .collect();
      34            0 : 
      35            0 :     Ok(databases)
      36            0 : }
      37              : 
      38              : /// Connect to every database (see list_dbs above) and get the list of installed extensions.
      39              : ///
      40              : /// Same extension can be installed in multiple databases with different versions,
      41              : /// we only keep the highest and lowest version across all databases.
      42            0 : pub fn get_installed_extensions(mut conf: postgres::config::Config) -> Result<InstalledExtensions> {
      43            0 :     conf.application_name("compute_ctl:get_installed_extensions");
      44            0 :     let mut client = conf.connect(NoTls)?;
      45              : 
      46            0 :     let databases: Vec<String> = list_dbs(&mut client)?;
      47              : 
      48            0 :     let mut extensions_map: HashMap<String, InstalledExtension> = HashMap::new();
      49            0 :     for db in databases.iter() {
      50            0 :         conf.dbname(db);
      51            0 :         let mut db_client = conf.connect(NoTls)?;
      52            0 :         let extensions: Vec<(String, String)> = db_client
      53            0 :             .query(
      54            0 :                 "SELECT extname, extversion FROM pg_catalog.pg_extension;",
      55            0 :                 &[],
      56            0 :             )?
      57            0 :             .iter()
      58            0 :             .map(|row| (row.get("extname"), row.get("extversion")))
      59            0 :             .collect();
      60              : 
      61            0 :         for (extname, v) in extensions.iter() {
      62            0 :             let version = v.to_string();
      63            0 : 
      64            0 :             // increment the number of databases where the version of extension is installed
      65            0 :             INSTALLED_EXTENSIONS
      66            0 :                 .with_label_values(&[extname, &version])
      67            0 :                 .inc();
      68            0 : 
      69            0 :             extensions_map
      70            0 :                 .entry(extname.to_string())
      71            0 :                 .and_modify(|e| {
      72            0 :                     e.versions.insert(version.clone());
      73            0 :                     // count the number of databases where the extension is installed
      74            0 :                     e.n_databases += 1;
      75            0 :                 })
      76            0 :                 .or_insert(InstalledExtension {
      77            0 :                     extname: extname.to_string(),
      78            0 :                     versions: HashSet::from([version.clone()]),
      79            0 :                     n_databases: 1,
      80            0 :                 });
      81            0 :         }
      82              :     }
      83              : 
      84            0 :     let res = InstalledExtensions {
      85            0 :         extensions: extensions_map.into_values().collect(),
      86            0 :     };
      87            0 : 
      88            0 :     Ok(res)
      89            0 : }
      90              : 
      91            0 : static INSTALLED_EXTENSIONS: Lazy<UIntGaugeVec> = Lazy::new(|| {
      92            0 :     register_uint_gauge_vec!(
      93            0 :         "compute_installed_extensions",
      94            0 :         "Number of databases where the version of extension is installed",
      95            0 :         &["extension_name", "version"]
      96            0 :     )
      97            0 :     .expect("failed to define a metric")
      98            0 : });
      99              : 
     100            0 : pub fn collect() -> Vec<MetricFamily> {
     101            0 :     INSTALLED_EXTENSIONS.collect()
     102            0 : }
        

Generated by: LCOV version 2.1-beta