Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stats: export vttablet_tablet_type to prometheus #14303

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions changelog/19.0/19.0.0/summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
- **[Deprecations and Deletions](#deprecations-and-deletions)**
- **[Docker](#docker)**
- [New MySQL Image](#mysql-image)
- **[New stats](#new-stats)**
- [VTTablet tablet type for Prometheus](#vttablet-tablet-type-for-prometheus)

## <a id="major-changes"/>Major Changes

Expand All @@ -21,3 +23,21 @@ In `v19.0` the Vitess team is shipping a new image: `vitess/mysql`.
This lightweight image is a replacement of `vitess/lite` to only run `mysqld`.

Several tags are available to let you choose what version of MySQL you want to use: `vitess/mysql:8.0.30`, `vitess/mysql:8.0.34`.

### <a id="new-stats"/> New stats


#### <a id="vttablet-tablet-type-for-prometheus"/> VTTablet tablet type for Prometheus

VTTablet publishes the `TabletType` status which reports the current type of the tablet. For example, here's what it looks like in the local cluster example after setting up the initial cluster:

```
"TabletType": "primary"
```

Prior to v19, this data was not available via the Prometheus backend. In v19, this is also published as a Prometheus gauge with a `tablet_type` label and a value of `1.0`. For example:

```
$ curl -s localhost:15100/metrics | grep tablet_type{
vttablet_tablet_type{tablet_type="primary"} 1
```
57 changes: 49 additions & 8 deletions go/stats/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,47 @@ func pushOne(name string, v Variable) error {
return backend.PushOne(name, v)
}

// StringValueWithLabel is string publisher.
//
// The valueLabel is intended for use by backends which must export a stats in
// numeric form, and is not used by expvars.
//
// Backends which need to provide a numeric value can set a constant value of 1
// (or whatever is appropriate for the backend).
type StringValueWithLabel struct {
*StringValue
help string
label string
}

// Help returns the descriptive help message.
func (s StringValueWithLabel) Help() string {
return s.help
}

// Label returns the value label.
func (s StringValueWithLabel) Label() string {
return s.label
}

// NewStringValueWithLabel creates a new StringFuncWithValueLabel.
func NewStringValueWithLabel(name, help string, label, initialValue string) *StringValueWithLabel {
s := NewStringValue("" /* omit name to avoid publish conflicts */)
s.Set(initialValue)

t := &StringValueWithLabel{
StringValue: s,
help: help,
label: label,
}

if name != "" {
publish(name, t)
}

return t
}

// StringMapFuncWithMultiLabels is a multidimensional string map publisher.
//
// Map keys are compound names made with joining multiple strings with '.',
Expand Down Expand Up @@ -270,36 +311,36 @@ func (f FloatFunc) String() string {
return strconv.FormatFloat(f(), 'g', -1, 64)
}

// String is expvar.String+Get+hook
type String struct {
// StringValue is expvar.StringValue+Get+hook
type StringValue struct {
mu sync.Mutex
s string
}

// NewString returns a new String
func NewString(name string) *String {
v := new(String)
// NewStringValue returns a new String
func NewStringValue(name string) *StringValue {
v := new(StringValue)
publish(name, v)
return v
}

// Set sets the value
func (v *String) Set(value string) {
func (v *StringValue) Set(value string) {
v.mu.Lock()
v.s = value
v.mu.Unlock()
}

// Get returns the value
func (v *String) Get() string {
func (v *StringValue) Get() string {
v.mu.Lock()
s := v.s
v.mu.Unlock()
return s
}

// String is the implementation of expvar.var
func (v *String) String() string {
func (v *StringValue) String() string {
return strconv.Quote(v.Get())
}

Expand Down
21 changes: 18 additions & 3 deletions go/stats/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ func TestNoHook(t *testing.T) {

func TestString(t *testing.T) {
var gotname string
var gotv *String
var gotv *StringValue
clearStats()
Register(func(name string, v expvar.Var) {
gotname = name
gotv = v.(*String)
gotv = v.(*StringValue)
})
v := NewString("String")
v := NewStringValue("String")
if gotname != "String" {
t.Errorf("want String, got %s", gotname)
}
Expand Down Expand Up @@ -189,3 +189,18 @@ func TestStringMapWithMultiLabels(t *testing.T) {

require.Equal(t, c.ValueLabel(), "ccc")
}

func TestStringValueWithLabel(t *testing.T) {
clearStats()
c := NewStringValueWithLabel("stringValue1", "help", "aaa", "ccc")

s := c.String()
require.Equal(t, "\"ccc\"", s)

label := c.Label()
require.Equal(t, "aaa", label)

c.Set("ddd")
s = c.String()
require.Equal(t, "\"ddd\"", s)
}
33 changes: 33 additions & 0 deletions go/stats/prometheusbackend/collectors.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,3 +431,36 @@ func (c *stringMapFuncWithMultiLabelsCollector) Collect(ch chan<- prometheus.Met
}
}
}

type stringValueWithLabelCollector struct {
svl *stats.StringValueWithLabel
desc *prometheus.Desc
}

func newStringValueWithLabelCollector(svl *stats.StringValueWithLabel, name string) {
c := &stringValueWithLabelCollector{
svl: svl,
desc: prometheus.NewDesc(
name,
svl.Help(),
labelsToSnake([]string{svl.Label()}),
nil),
}

prometheus.MustRegister(c)
}

// Describe implements Collector.
func (c *stringValueWithLabelCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- c.desc
}

// Collect implements Collector.
func (c *stringValueWithLabelCollector) Collect(ch chan<- prometheus.Metric) {
metric, err := prometheus.NewConstMetric(c.desc, prometheus.GaugeValue, 1.0, c.svl.Get())
if err != nil {
log.Errorf("Error adding metric: %s", c.desc)
} else {
ch <- metric
}
}
4 changes: 3 additions & 1 deletion go/stats/prometheusbackend/prometheusbackend.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ func (be PromBackend) publishPrometheusMetric(name string, v expvar.Var) {
newHistogramCollector(st, be.buildPromName(name))
case *stats.StringMapFuncWithMultiLabels:
newStringMapFuncWithMultiLabelsCollector(st, be.buildPromName(name))
case *stats.String, stats.StringFunc, stats.StringMapFunc, *stats.Rates, *stats.RatesFunc:
case *stats.StringValueWithLabel:
newStringValueWithLabelCollector(st, be.buildPromName(name))
case *stats.StringValue, stats.StringFunc, stats.StringMapFunc, *stats.Rates, *stats.RatesFunc:
// Silently ignore these types since they don't make sense to
// export to Prometheus' data model.
default:
Expand Down
10 changes: 10 additions & 0 deletions go/stats/prometheusbackend/prometheusbackend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,16 @@ func TestPrometheusStringMapFuncWithMultiLabels(t *testing.T) {
checkHandlerForMetricWithMultiLabels(t, name, allLabels, []string{"bar", "baz", "world"}, 1)
}

func TestPrometheusStringValueWithLabel(t *testing.T) {
name := "blah_stringvaluewithlabel"
label := "label"
value := "value"

stats.NewStringValueWithLabel(name, "help", label, value)

checkHandlerForMetricWithMultiLabels(t, name, []string{label}, []string{value}, 1)
}

func checkHandlerForMetricWithMultiLabels(t *testing.T, metric string, labels []string, labelValues []string, value int64) {
response := testMetricsHandler(t)

Expand Down
2 changes: 1 addition & 1 deletion go/stats/statsd/statsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ func (sb StatsBackend) addExpVar(kv expvar.KeyValue) {
}
}
}
case *stats.String:
case *stats.StringValue:
if k == "BuildGitRev" {
buildGitRecOnce.Do(func() {
checksum := crc32.ChecksumIEEE([]byte(v.Get()))
Expand Down
2 changes: 1 addition & 1 deletion go/vt/grpccommon/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,5 @@ func MaxMessageSize() int {
}

func init() {
stats.NewString("GrpcVersion").Set(grpc.Version)
stats.NewStringValue("GrpcVersion").Set(grpc.Version)
}
14 changes: 7 additions & 7 deletions go/vt/servenv/buildinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,15 @@ func init() {
goArch: runtime.GOARCH,
version: versionName,
}
stats.NewString("BuildHost").Set(AppVersion.buildHost)
stats.NewString("BuildUser").Set(AppVersion.buildUser)
stats.NewStringValue("BuildHost").Set(AppVersion.buildHost)
stats.NewStringValue("BuildUser").Set(AppVersion.buildUser)
stats.NewGauge("BuildTimestamp", "build timestamp").Set(AppVersion.buildTime)
stats.NewString("BuildGitRev").Set(AppVersion.buildGitRev)
stats.NewString("BuildGitBranch").Set(AppVersion.buildGitBranch)
stats.NewStringValue("BuildGitRev").Set(AppVersion.buildGitRev)
stats.NewStringValue("BuildGitBranch").Set(AppVersion.buildGitBranch)
stats.NewGauge("BuildNumber", "build number").Set(AppVersion.jenkinsBuildNumber)
stats.NewString("GoVersion").Set(AppVersion.goVersion)
stats.NewString("GoOS").Set(AppVersion.goOS)
stats.NewString("GoArch").Set(AppVersion.goArch)
stats.NewStringValue("GoVersion").Set(AppVersion.goVersion)
stats.NewStringValue("GoOS").Set(AppVersion.goOS)
stats.NewStringValue("GoArch").Set(AppVersion.goArch)

buildLabels := []string{"BuildHost", "BuildUser", "BuildTimestamp", "BuildGitRev", "BuildGitBranch", "BuildNumber"}
buildValues := []string{
Expand Down
2 changes: 1 addition & 1 deletion go/vt/servenv/exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ func TestHistogram(t *testing.T) {

func TestPublish(t *testing.T) {
ebd := NewExporter("", "")
s := stats.NewString("")
s := stats.NewStringValue("")
ebd.Publish("gpub", s)
s.Set("1")
assert.Equal(t, `"1"`, expvar.Get("gpub").String())
Expand Down
8 changes: 4 additions & 4 deletions go/vt/vttablet/tabletmanager/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ var (
restoreConcurrency = 4
waitForBackupInterval time.Duration

statsRestoreBackupTime *stats.String
statsRestoreBackupPosition *stats.String
statsRestoreBackupTime *stats.StringValue
statsRestoreBackupPosition *stats.StringValue
)

func registerRestoreFlags(fs *pflag.FlagSet) {
Expand Down Expand Up @@ -116,8 +116,8 @@ func init() {
servenv.OnParseFor("vtcombo", registerPointInTimeRestoreFlags)
servenv.OnParseFor("vttablet", registerPointInTimeRestoreFlags)

statsRestoreBackupTime = stats.NewString("RestoredBackupTime")
statsRestoreBackupPosition = stats.NewString("RestorePosition")
statsRestoreBackupTime = stats.NewStringValue("RestoredBackupTime")
statsRestoreBackupPosition = stats.NewStringValue("RestorePosition")
}

// RestoreData is the main entry point for backup restore.
Expand Down
14 changes: 7 additions & 7 deletions go/vt/vttablet/tabletmanager/tm_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func registerInitFlags(fs *pflag.FlagSet) {

var (
// statsTabletType is set to expose the current tablet type.
statsTabletType *stats.String
statsTabletType *stats.StringValueWithLabel

// statsTabletTypeCount exposes the current tablet type as a label,
// with the value counting the occurrences of the respective tablet type.
Expand All @@ -116,11 +116,11 @@ var (
// statsIsInSrvKeyspace is set to 1 (true), 0 (false) whether the tablet is in the serving keyspace
statsIsInSrvKeyspace *stats.Gauge

statsKeyspace = stats.NewString("TabletKeyspace")
statsShard = stats.NewString("TabletShard")
statsKeyRangeStart = stats.NewString("TabletKeyRangeStart")
statsKeyRangeEnd = stats.NewString("TabletKeyRangeEnd")
statsAlias = stats.NewString("TabletAlias")
statsKeyspace = stats.NewStringValue("TabletKeyspace")
statsShard = stats.NewStringValue("TabletShard")
statsKeyRangeStart = stats.NewStringValue("TabletKeyRangeStart")
statsKeyRangeEnd = stats.NewStringValue("TabletKeyRangeEnd")
statsAlias = stats.NewStringValue("TabletAlias")

// The following variables can be changed to speed up tests.
mysqlPortRetryInterval = 1 * time.Second
Expand All @@ -131,7 +131,7 @@ func init() {
servenv.OnParseFor("vtcombo", registerInitFlags)
servenv.OnParseFor("vttablet", registerInitFlags)

statsTabletType = stats.NewString("TabletType")
statsTabletType = stats.NewStringValueWithLabel("TabletType", "Current tablet type", "TabletType", "")
statsTabletTypeCount = stats.NewCountersWithSingleLabel("TabletTypeCount", "Number of times the tablet changed to the labeled type", "type")
statsBackupIsRunning = stats.NewGaugesWithMultiLabels("BackupIsRunning", "Whether a backup is running", []string{"mode"})
statsIsInSrvKeyspace = stats.NewGauge("IsInSrvKeyspace", "Whether the vttablet is in the serving keyspace (1 = true / 0 = false)")
Expand Down
Loading