...

Source file src/github.com/cybertec-postgresql/pgwatch/v5/internal/reaper/file.go

Documentation: github.com/cybertec-postgresql/pgwatch/v5/internal/reaper

     1  package reaper
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  
     7  	"slices"
     8  
     9  	"github.com/cybertec-postgresql/pgwatch/v5/internal/metrics"
    10  	"github.com/cybertec-postgresql/pgwatch/v5/internal/sources"
    11  )
    12  
    13  func DoesEmergencyTriggerfileExist(fname string) bool {
    14  	// Main idea of the feature is to be able to quickly free monitored DBs / network of any extra "monitoring effect" load.
    15  	// In highly automated K8s / IaC environments such a temporary change might involve pull requests, peer reviews, CI/CD etc
    16  	// which can all take too long vs "exec -it pgwatch-pod -- touch /tmp/pgwatch-emergency-pause".
    17  	// After creating the file it can still take up to --servers-refresh-loop-seconds (2min def.) for change to take effect!
    18  	if fname == "" {
    19  		return false
    20  	}
    21  	_, err := os.Stat(fname)
    22  	return err == nil
    23  }
    24  
    25  const (
    26  	metricCPULoad           = "cpu_load"
    27  	metricPsutilCPU         = "psutil_cpu"
    28  	metricPsutilDisk        = "psutil_disk"
    29  	metricPsutilDiskIoTotal = "psutil_disk_io_total"
    30  	metricPsutilMem         = "psutil_mem"
    31  )
    32  
    33  const sqlPgDirs = `select name, path from 
    34  (values 
    35  	('data_directory', current_setting('data_directory')),
    36  	('pg_wal', current_setting('data_directory')||'/pg_wal'),
    37  	('log_directory', case 
    38          when current_setting('log_directory') ~ '^(\w:)?\/.+' then current_setting('log_directory') 
    39          else current_setting('data_directory') || '/' || current_setting('log_directory') 
    40      end)) as d(name, path)
    41  union all
    42  select spcname::text, pg_catalog.pg_tablespace_location(oid)
    43  from pg_catalog.pg_tablespace where spcname !~ 'pg_.+'`
    44  
    45  var directlyFetchableOSMetrics = []string{metricPsutilCPU, metricPsutilDisk, metricPsutilDiskIoTotal, metricPsutilMem, metricCPULoad}
    46  
    47  func IsDirectlyFetchableMetric(md *sources.SourceConn, metric string) bool {
    48  	return slices.Contains(directlyFetchableOSMetrics, metric) && md.IsClientOnSameHost()
    49  }
    50  
    51  func (r *Reaper) FetchStatsDirectlyFromOS(ctx context.Context, md *sources.SourceConn, metricName string) (*metrics.MeasurementEnvelope, error) {
    52  	var data, pgDirs metrics.Measurements
    53  	var err error
    54  
    55  	switch metricName {
    56  	case metricCPULoad:
    57  		data, err = GetLoadAvgLocal()
    58  	case metricPsutilCPU:
    59  		data, err = GetGoPsutilCPU(md.GetMetricInterval(metricName))
    60  	case metricPsutilDisk:
    61  		if pgDirs, err = QueryMeasurements(ctx, md, sqlPgDirs); err != nil {
    62  			return nil, err
    63  		}
    64  		data, err = GetGoPsutilDiskPG(pgDirs)
    65  	case metricPsutilDiskIoTotal:
    66  		data, err = GetGoPsutilDiskTotals()
    67  	case metricPsutilMem:
    68  		data, err = GetGoPsutilMem()
    69  	}
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	return &metrics.MeasurementEnvelope{
    74  		DBName:     md.Name,
    75  		MetricName: metricName,
    76  		CustomTags: md.CustomTags,
    77  		Data:       data,
    78  	}, nil
    79  }
    80