Skip to content

Commit 98ba566

Browse files
mattbostockwrouesnel
authored andcommitted
Use the pg_settings view to retrieve runtime variable
Adds all bool/integer/real runtime variables that can be retrieved ths way. Use the `pg_settings` view to retrieve runtime variables: https://www.postgresql.org/docs/current/static/view-pg-settings.html Replaces the use of `SHOW` to retrieve runtime variables. Only runtime variables with a `vartype` of `bool`, `real`, or `integer` are currently supported. Uses the `short_desc` field as a description. This commit deprecates the following metric names: ``` pg_runtime_variable_max_connections pg_runtime_variable_max_files_per_process pg_runtime_variable_max_function_args pg_runtime_variable_max_identifier_length pg_runtime_variable_max_index_keys pg_runtime_variable_max_locks_per_transaction pg_runtime_variable_max_pred_locks_per_transaction pg_runtime_variable_max_prepared_transactions pg_runtime_variable_max_standby_archive_delay_milliseconds pg_runtime_variable_max_standby_streaming_delay_milliseconds pg_runtime_variable_max_wal_senders ``` They are replaced by equivalent names under `pg_settings` with the exception of ``` pg_runtime_variable_max_standby_archive_delay_milliseconds pg_runtime_variable_max_standby_streaming_delay_milliseconds ``` which are replaced with ``` pg_settings_max_standby_archive_delay_seconds pg_settings_max_standby_streaming_delay_seconds ``` Adds approximately 195 new metrics, when considered across all supported PostgreSQL versions.
1 parent 994be31 commit 98ba566

4 files changed

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

0 commit comments

Comments
 (0)