github.com/netdata/go.d.plugin@v0.58.1/modules/postgres/postgres.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package postgres 4 5 import ( 6 "database/sql" 7 _ "embed" 8 "sync" 9 "time" 10 11 "github.com/netdata/go.d.plugin/agent/module" 12 "github.com/netdata/go.d.plugin/pkg/matcher" 13 "github.com/netdata/go.d.plugin/pkg/metrics" 14 "github.com/netdata/go.d.plugin/pkg/web" 15 16 "github.com/jackc/pgx/v4/stdlib" 17 _ "github.com/jackc/pgx/v4/stdlib" 18 ) 19 20 //go:embed "config_schema.json" 21 var configSchema string 22 23 func init() { 24 module.Register("postgres", module.Creator{ 25 JobConfigSchema: configSchema, 26 Create: func() module.Module { return New() }, 27 }) 28 } 29 30 func New() *Postgres { 31 return &Postgres{ 32 Config: Config{ 33 Timeout: web.Duration{Duration: time.Second * 2}, 34 DSN: "postgres://postgres:postgres@127.0.0.1:5432/postgres", 35 XactTimeHistogram: []float64{.1, .5, 1, 2.5, 5, 10}, 36 QueryTimeHistogram: []float64{.1, .5, 1, 2.5, 5, 10}, 37 // charts: 20 x table, 4 x index. 38 // https://discord.com/channels/847502280503590932/1022693928874549368 39 MaxDBTables: 50, 40 MaxDBIndexes: 250, 41 }, 42 charts: baseCharts.Copy(), 43 dbConns: make(map[string]*dbConn), 44 mx: &pgMetrics{ 45 dbs: make(map[string]*dbMetrics), 46 indexes: make(map[string]*indexMetrics), 47 tables: make(map[string]*tableMetrics), 48 replApps: make(map[string]*replStandbyAppMetrics), 49 replSlots: make(map[string]*replSlotMetrics), 50 }, 51 recheckSettingsEvery: time.Minute * 30, 52 doSlowEvery: time.Minute * 5, 53 addXactQueryRunningTimeChartsOnce: &sync.Once{}, 54 addWALFilesChartsOnce: &sync.Once{}, 55 } 56 } 57 58 type Config struct { 59 DSN string `yaml:"dsn"` 60 Timeout web.Duration `yaml:"timeout"` 61 DBSelector string `yaml:"collect_databases_matching"` 62 XactTimeHistogram []float64 `yaml:"transaction_time_histogram"` 63 QueryTimeHistogram []float64 `yaml:"query_time_histogram"` 64 MaxDBTables int64 `yaml:"max_db_tables"` 65 MaxDBIndexes int64 `yaml:"max_db_indexes"` 66 } 67 68 type ( 69 Postgres struct { 70 module.Base 71 Config `yaml:",inline"` 72 73 charts *module.Charts 74 75 db *sql.DB 76 dbConns map[string]*dbConn 77 78 superUser *bool 79 pgIsInRecovery *bool 80 pgVersion int 81 82 addXactQueryRunningTimeChartsOnce *sync.Once 83 addWALFilesChartsOnce *sync.Once 84 85 dbSr matcher.Matcher 86 87 mx *pgMetrics 88 89 recheckSettingsTime time.Time 90 recheckSettingsEvery time.Duration 91 92 doSlowTime time.Time 93 doSlowEvery time.Duration 94 } 95 dbConn struct { 96 db *sql.DB 97 connStr string 98 connErrors int 99 } 100 ) 101 102 func (p *Postgres) Init() bool { 103 err := p.validateConfig() 104 if err != nil { 105 p.Errorf("config validation: %v", err) 106 return false 107 } 108 109 sr, err := p.initDBSelector() 110 if err != nil { 111 p.Errorf("config validation: %v", err) 112 return false 113 } 114 p.dbSr = sr 115 116 p.mx.xactTimeHist = metrics.NewHistogramWithRangeBuckets(p.XactTimeHistogram) 117 p.mx.queryTimeHist = metrics.NewHistogramWithRangeBuckets(p.QueryTimeHistogram) 118 119 return true 120 } 121 122 func (p *Postgres) Check() bool { 123 return len(p.Collect()) > 0 124 } 125 126 func (p *Postgres) Charts() *module.Charts { 127 return p.charts 128 } 129 130 func (p *Postgres) Collect() map[string]int64 { 131 mx, err := p.collect() 132 if err != nil { 133 p.Error(err) 134 } 135 136 if len(mx) == 0 { 137 return nil 138 } 139 return mx 140 } 141 142 func (p *Postgres) Cleanup() { 143 if p.db == nil { 144 return 145 } 146 if err := p.db.Close(); err != nil { 147 p.Warningf("cleanup: error on closing the Postgres database [%s]: %v", p.DSN, err) 148 } 149 p.db = nil 150 151 for dbname, conn := range p.dbConns { 152 delete(p.dbConns, dbname) 153 if conn.connStr != "" { 154 stdlib.UnregisterConnConfig(conn.connStr) 155 } 156 if conn.db != nil { 157 _ = conn.db.Close() 158 } 159 } 160 }