Skip to content

Commit 6fb45dc

Browse files
committed
Add all bool/integer/real runtime variables
Use the `pg_settings` view to retrieve runtime variables: https://www.postgresql.org/docs/current/static/view-pg-settings.html This replaces the use of `SHOW` to retrieve runtime variables. In PostgreSQL 9.6, this adds 189 metrics, which use the `short_desc` field as a description. Only runtime variables with a `vartype` of `bool`, `real`, or `integer` are currently supported. Example metrics: # HELP pg_settings_allow_system_table_mods Allows modifications of the structure of system tables. # TYPE pg_settings_allow_system_table_mods gauge pg_settings_allow_system_table_mods 0 # HELP pg_settings_archive_timeout_seconds Forces a switch to the next xlog file if a new file has not been started within N seconds. [Units converted to seconds.] # TYPE pg_settings_archive_timeout_seconds gauge pg_settings_archive_timeout_seconds 0 # HELP pg_settings_array_nulls Enable input of NULL elements in arrays. # TYPE pg_settings_array_nulls gauge pg_settings_array_nulls 1 # HELP pg_settings_authentication_timeout_seconds Sets the maximum allowed time to complete client authentication. [Units converted to seconds.] # TYPE pg_settings_authentication_timeout_seconds gauge pg_settings_authentication_timeout_seconds 60 # HELP pg_settings_autovacuum Starts the autovacuum subprocess. # TYPE pg_settings_autovacuum gauge pg_settings_autovacuum 1 # HELP pg_settings_autovacuum_analyze_scale_factor Number of tuple inserts, updates, or deletes prior to analyze as a fraction of reltuples. # TYPE pg_settings_autovacuum_analyze_scale_factor gauge pg_settings_autovacuum_analyze_scale_factor 0.1 # HELP pg_settings_autovacuum_analyze_threshold Minimum number of tuple inserts, updates, or deletes prior to analyze. # TYPE pg_settings_autovacuum_analyze_threshold gauge pg_settings_autovacuum_analyze_threshold 50 # HELP pg_settings_autovacuum_freeze_max_age Age at which to autovacuum a table to prevent transaction ID wraparound. # TYPE pg_settings_autovacuum_freeze_max_age gauge pg_settings_autovacuum_freeze_max_age 2e+08 # HELP pg_settings_autovacuum_max_workers Sets the maximum number of simultaneously running autovacuum worker processes. # TYPE pg_settings_autovacuum_max_workers gauge pg_settings_autovacuum_max_workers 3 # HELP pg_settings_autovacuum_multixact_freeze_max_age Multixact age at which to autovacuum a table to prevent multixact wraparound. # TYPE pg_settings_autovacuum_multixact_freeze_max_age gauge pg_settings_autovacuum_multixact_freeze_max_age 4e+08 # HELP pg_settings_autovacuum_naptime_seconds Time to sleep between autovacuum runs. [Units converted to seconds.] # TYPE pg_settings_autovacuum_naptime_seconds gauge pg_settings_autovacuum_naptime_seconds 60 # HELP pg_settings_autovacuum_vacuum_cost_delay_seconds Vacuum cost delay in milliseconds, for autovacuum. [Units converted to seconds.] # TYPE pg_settings_autovacuum_vacuum_cost_delay_seconds gauge pg_settings_autovacuum_vacuum_cost_delay_seconds 0.02 # HELP pg_settings_autovacuum_vacuum_cost_limit Vacuum cost amount available before napping, for autovacuum. # TYPE pg_settings_autovacuum_vacuum_cost_limit gauge pg_settings_autovacuum_vacuum_cost_limit -1 # HELP pg_settings_autovacuum_vacuum_scale_factor Number of tuple updates or deletes prior to vacuum as a fraction of reltuples. # TYPE pg_settings_autovacuum_vacuum_scale_factor gauge pg_settings_autovacuum_vacuum_scale_factor 0.2 # HELP pg_settings_autovacuum_vacuum_threshold Minimum number of tuple updates or deletes prior to vacuum. # TYPE pg_settings_autovacuum_vacuum_threshold gauge pg_settings_autovacuum_vacuum_threshold 50 # HELP pg_settings_autovacuum_work_mem_bytes Sets the maximum memory to be used by each autovacuum worker process. [Units converted to bytes.] # TYPE pg_settings_autovacuum_work_mem_bytes gauge pg_settings_autovacuum_work_mem_bytes -1
1 parent 0b003b2 commit 6fb45dc

File tree

4 files changed

+330
-75
lines changed

4 files changed

+330
-75
lines changed

pg_setting.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package main
2+
3+
import (
4+
"database/sql"
5+
"errors"
6+
"fmt"
7+
"math"
8+
"strconv"
9+
10+
"code.cfops.it/go/log"
11+
"github.com/prometheus/client_golang/prometheus"
12+
)
13+
14+
// Query the pg_settings view containing runtime variables
15+
func querySettings(ch chan<- prometheus.Metric, db *sql.DB) error {
16+
log.Debugln("Querying pg_setting view")
17+
18+
// pg_settings docs: https://www.postgresql.org/docs/current/static/view-pg-settings.html
19+
//
20+
// NOTE: If you add more vartypes here, you must update the supported
21+
// types in normaliseUnit() below
22+
query := "SELECT name, setting, COALESCE(unit, ''), short_desc, vartype FROM pg_settings WHERE vartype IN ('bool', 'integer', 'real');"
23+
24+
rows, err := db.Query(query)
25+
if err != nil {
26+
return errors.New(fmt.Sprintln("Error running query on database: ", namespace, err))
27+
}
28+
defer rows.Close()
29+
30+
for rows.Next() {
31+
s := &pgSetting{}
32+
err = rows.Scan(&s.name, &s.setting, &s.unit, &s.shortDesc, &s.vartype)
33+
if err != nil {
34+
return errors.New(fmt.Sprintln("Error retrieving rows:", namespace, err))
35+
}
36+
37+
ch <- s.metric()
38+
}
39+
40+
return nil
41+
}
42+
43+
// pgSetting is represents a PostgreSQL runtime variable as returned by the
44+
// pg_settings view.
45+
type pgSetting struct {
46+
name, setting, unit, shortDesc, vartype string
47+
}
48+
49+
func (s *pgSetting) metric() prometheus.Metric {
50+
var (
51+
err error
52+
name = s.name
53+
unit = s.unit
54+
shortDesc = s.shortDesc
55+
subsystem = "settings"
56+
val float64
57+
)
58+
59+
switch s.vartype {
60+
case "bool":
61+
if s.setting == "on" {
62+
val = 1
63+
}
64+
case "integer", "real":
65+
if val, unit, err = s.normaliseUnit(); err != nil {
66+
// Panic, since we should recognise all units
67+
// and don't want to silently exlude metrics
68+
panic(err)
69+
}
70+
71+
if len(unit) > 0 {
72+
name = fmt.Sprintf("%s_%s", name, unit)
73+
shortDesc = fmt.Sprintf("%s [Units converted to %s.]", shortDesc, unit)
74+
}
75+
default:
76+
// Panic because we got a type we didn't ask for
77+
panic(fmt.Sprintf("Unsupported vartype %q", s.vartype))
78+
}
79+
80+
desc := newDesc(subsystem, name, shortDesc)
81+
return prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, val)
82+
}
83+
84+
func (s *pgSetting) normaliseUnit() (val float64, unit string, err error) {
85+
val, err = strconv.ParseFloat(s.setting, 64)
86+
if err != nil {
87+
return val, unit, errors.New(fmt.Sprintf("Error converting setting %q value %q to float: %s", s.name, s.setting, err))
88+
}
89+
90+
// Units defined in: https://www.postgresql.org/docs/current/static/config-setting.html
91+
switch s.unit {
92+
case "":
93+
return
94+
case "ms", "s", "min", "h", "d":
95+
unit = "seconds"
96+
case "kB", "MB", "GB", "TB", "8kB", "16MB":
97+
unit = "bytes"
98+
default:
99+
err = errors.New(fmt.Sprintf("Unknown unit for runtime variable: %q", s.unit))
100+
return
101+
}
102+
103+
// -1 is special, don't modify the value
104+
if val == -1 {
105+
return
106+
}
107+
108+
switch s.unit {
109+
case "ms":
110+
val /= 1000
111+
case "min":
112+
val *= 60
113+
case "h":
114+
val *= 60 * 60
115+
case "d":
116+
val *= 60 * 60 * 24
117+
case "kB":
118+
val *= math.Pow(2, 10)
119+
case "MB":
120+
val *= math.Pow(2, 20)
121+
case "GB":
122+
val *= math.Pow(2, 30)
123+
case "TB":
124+
val *= math.Pow(2, 40)
125+
case "8kB":
126+
val *= math.Pow(2, 13)
127+
case "16MB":
128+
val *= math.Pow(2, 24)
129+
}
130+
131+
return
132+
}

pg_setting_test.go

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
// +build !integration
2+
package main
3+
4+
import (
5+
dto "github.com/prometheus/client_model/go"
6+
. "gopkg.in/check.v1"
7+
)
8+
9+
type PgSettingSuite struct{}
10+
11+
var _ = Suite(&PgSettingSuite{})
12+
13+
var fixtures = []fixture{
14+
fixture{
15+
p: pgSetting{
16+
name: "seconds_fixture_metric",
17+
setting: "5",
18+
unit: "s",
19+
shortDesc: "Foo foo foo",
20+
vartype: "integer",
21+
},
22+
n: normalised{
23+
val: 5,
24+
unit: "seconds",
25+
err: "",
26+
},
27+
d: "Desc{fqName: \"pg_settings_seconds_fixture_metric_seconds\", help: \"Foo foo foo [Units converted to seconds.]\", constLabels: {}, variableLabels: []}",
28+
v: 5,
29+
},
30+
fixture{
31+
p: pgSetting{
32+
name: "milliseconds_fixture_metric",
33+
setting: "5000",
34+
unit: "ms",
35+
shortDesc: "Foo foo foo",
36+
vartype: "integer",
37+
},
38+
n: normalised{
39+
val: 5,
40+
unit: "seconds",
41+
err: "",
42+
},
43+
d: "Desc{fqName: \"pg_settings_milliseconds_fixture_metric_seconds\", help: \"Foo foo foo [Units converted to seconds.]\", constLabels: {}, variableLabels: []}",
44+
v: 5,
45+
},
46+
fixture{
47+
p: pgSetting{
48+
name: "eight_kb_fixture_metric",
49+
setting: "17",
50+
unit: "8kB",
51+
shortDesc: "Foo foo foo",
52+
vartype: "integer",
53+
},
54+
n: normalised{
55+
val: 139264,
56+
unit: "bytes",
57+
err: "",
58+
},
59+
d: "Desc{fqName: \"pg_settings_eight_kb_fixture_metric_bytes\", help: \"Foo foo foo [Units converted to bytes.]\", constLabels: {}, variableLabels: []}",
60+
v: 139264,
61+
},
62+
fixture{
63+
p: pgSetting{
64+
name: "16_mb_real_fixture_metric",
65+
setting: "3.0",
66+
unit: "16MB",
67+
shortDesc: "Foo foo foo",
68+
vartype: "real",
69+
},
70+
n: normalised{
71+
val: 5.0331648e+07,
72+
unit: "bytes",
73+
err: "",
74+
},
75+
d: "Desc{fqName: \"pg_settings_16_mb_real_fixture_metric_bytes\", help: \"Foo foo foo [Units converted to bytes.]\", constLabels: {}, variableLabels: []}",
76+
v: 5.0331648e+07,
77+
},
78+
fixture{
79+
p: pgSetting{
80+
name: "bool_on_fixture_metric",
81+
setting: "on",
82+
unit: "",
83+
shortDesc: "Foo foo foo",
84+
vartype: "bool",
85+
},
86+
n: normalised{
87+
val: 1,
88+
unit: "",
89+
err: "",
90+
},
91+
d: "Desc{fqName: \"pg_settings_bool_on_fixture_metric\", help: \"Foo foo foo\", constLabels: {}, variableLabels: []}",
92+
v: 1,
93+
},
94+
fixture{
95+
p: pgSetting{
96+
name: "bool_off_fixture_metric",
97+
setting: "off",
98+
unit: "",
99+
shortDesc: "Foo foo foo",
100+
vartype: "bool",
101+
},
102+
n: normalised{
103+
val: 0,
104+
unit: "",
105+
err: "",
106+
},
107+
d: "Desc{fqName: \"pg_settings_bool_off_fixture_metric\", help: \"Foo foo foo\", constLabels: {}, variableLabels: []}",
108+
v: 0,
109+
},
110+
fixture{
111+
p: pgSetting{
112+
name: "special_minus_one_value",
113+
setting: "-1",
114+
unit: "d",
115+
shortDesc: "foo foo foo",
116+
vartype: "integer",
117+
},
118+
n: normalised{
119+
val: -1,
120+
unit: "seconds",
121+
err: "",
122+
},
123+
d: "Desc{fqName: \"pg_settings_special_minus_one_value_seconds\", help: \"foo foo foo [Units converted to seconds.]\", constLabels: {}, variableLabels: []}",
124+
v: -1,
125+
},
126+
fixture{
127+
p: pgSetting{
128+
name: "unknown_unit",
129+
setting: "10",
130+
unit: "nonexistent",
131+
shortDesc: "foo foo foo",
132+
vartype: "integer",
133+
},
134+
n: normalised{
135+
val: 10,
136+
unit: "",
137+
err: `Unknown unit for runtime variable: "nonexistent"`,
138+
},
139+
},
140+
}
141+
142+
func (s *PgSettingSuite) TestNormaliseUnit(c *C) {
143+
for _, f := range fixtures {
144+
switch f.p.vartype {
145+
case "integer", "real":
146+
val, unit, err := f.p.normaliseUnit()
147+
148+
c.Check(val, Equals, f.n.val)
149+
c.Check(unit, Equals, f.n.unit)
150+
151+
if err == nil {
152+
c.Check("", Equals, f.n.err)
153+
} else {
154+
c.Check(err.Error(), Equals, f.n.err)
155+
}
156+
}
157+
}
158+
}
159+
160+
func (s *PgSettingSuite) TestMetric(c *C) {
161+
defer func() {
162+
if r := recover(); r != nil {
163+
if r.(error).Error() != `Unknown unit for runtime variable: "nonexistent"` {
164+
panic(r)
165+
}
166+
}
167+
}()
168+
169+
for _, f := range fixtures {
170+
d := &dto.Metric{}
171+
m := f.p.metric()
172+
m.Write(d)
173+
174+
c.Check(m.Desc().String(), Equals, f.d)
175+
c.Check(d.GetGauge().GetValue(), Equals, f.v)
176+
}
177+
}
178+
179+
type normalised struct {
180+
val float64
181+
unit string
182+
err string
183+
}
184+
185+
type fixture struct {
186+
p pgSetting
187+
n normalised
188+
d string
189+
v float64
190+
}

0 commit comments

Comments
 (0)