Skip to content

Commit fa8f673

Browse files
authored
[Feature] Imporved metrics (#852)
1 parent bfa9e20 commit fa8f673

File tree

10 files changed

+443
-7
lines changed

10 files changed

+443
-7
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ require (
5151
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.44.1
5252
github.com/prometheus-operator/prometheus-operator/pkg/client v0.0.0-00010101000000-000000000000
5353
github.com/prometheus/client_golang v1.7.1
54+
github.com/prometheus/client_model v0.2.0
5455
github.com/robfig/cron v1.2.0
5556
github.com/rs/zerolog v1.19.0
5657
github.com/spf13/cobra v1.2.1

pkg/deployment/agency/cache.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,24 +90,24 @@ func (c *cache) Reload(ctx context.Context, client agency.Agency) (uint64, error
9090
c.lock.Lock()
9191
defer c.lock.Unlock()
9292

93-
c.valid = false
94-
9593
cfg, err := getAgencyConfig(ctx, client)
9694
if err != nil {
97-
return cfg.CommitIndex, err
95+
c.valid = false
96+
return 0, err
9897
}
9998

100-
if cfg.CommitIndex == c.commitIndex {
99+
if cfg.CommitIndex == c.commitIndex && c.valid {
101100
// We are on same index, nothing to do
102101
return cfg.CommitIndex, err
103102
}
104103

105104
if data, err := loadState(ctx, client); err != nil {
105+
c.valid = false
106106
return cfg.CommitIndex, err
107107
} else {
108108
c.data = data
109109
c.valid = true
110110
c.commitIndex = cfg.CommitIndex
111-
return cfg.CommitIndex, err
111+
return cfg.CommitIndex, nil
112112
}
113113
}

pkg/deployment/agency/plan_collections.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,18 @@ func (a StatePlanDBCollections) IsDBServerInCollections(name string) bool {
4343
}
4444

4545
type StatePlanCollection struct {
46+
Name *string `json:"name"`
4647
Shards StatePlanShard `json:"shards"`
4748
}
4849

50+
func (a StatePlanCollection) GetName(d string) string {
51+
if a.Name == nil {
52+
return d
53+
}
54+
55+
return *a.Name
56+
}
57+
4958
func (a StatePlanCollection) IsDBServerInShards(name string) bool {
5059
for _, dbservers := range a.Shards {
5160
for _, dbserver := range dbservers {

pkg/deployment/deployment.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ func New(config Config, deps Dependencies, apiObject *api.ArangoDeployment) (*De
226226
d.status.last.AcceptedSpec = apiObject.Spec.DeepCopy()
227227
}
228228

229+
localInventory.Add(d)
230+
229231
go d.run()
230232
go d.listenForPodEvents(d.stopCh)
231233
go d.listenForPVCEvents(d.stopCh)

pkg/deployment/metrics.go

Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,122 @@
1717
//
1818
// Copyright holder is ArangoDB GmbH, Cologne, Germany
1919
//
20-
// Author Ewout Prangsma
21-
//
2220

2321
package deployment
2422

23+
import (
24+
"sync"
25+
26+
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
27+
"github.com/arangodb/kube-arangodb/pkg/util/metrics"
28+
"github.com/prometheus/client_golang/prometheus"
29+
)
30+
2531
const (
2632
// Component name for metrics of this package
2733
metricsComponent = "deployment"
2834
)
35+
36+
func init() {
37+
localInventory = inventory{
38+
deployments: map[string]map[string]*Deployment{},
39+
deploymentsMetric: metrics.NewDescription("arangodb_operator_deployments", "Number of active deployments", []string{"namespace", "deployment"}, nil),
40+
deploymentMetricsMembersMetric: metrics.NewDescription("arango_operator_deployment_members", "List of members", []string{"namespace", "deployment", "role", "id"}, nil),
41+
deploymentAgencyStateMetric: metrics.NewDescription("arango_operator_deployment_agency_state", "Reachability of agency", []string{"namespace", "deployment"}, nil),
42+
deploymentShardLeadersMetric: metrics.NewDescription("arango_operator_deployment_shard_leaders", "Deployment leader shards distribution", []string{"namespace", "deployment", "database", "collection", "shard", "server"}, nil),
43+
deploymentShardsMetric: metrics.NewDescription("arango_operator_deployment_shards", "Deployment shards distribution", []string{"namespace", "deployment", "database", "collection", "shard", "server"}, nil),
44+
}
45+
46+
prometheus.MustRegister(&localInventory)
47+
}
48+
49+
var localInventory inventory
50+
51+
var _ prometheus.Collector = &inventory{}
52+
53+
type inventory struct {
54+
lock sync.Mutex
55+
deployments map[string]map[string]*Deployment
56+
57+
deploymentsMetric, deploymentMetricsMembersMetric, deploymentAgencyStateMetric, deploymentShardsMetric, deploymentShardLeadersMetric metrics.Description
58+
}
59+
60+
func (i *inventory) Describe(descs chan<- *prometheus.Desc) {
61+
i.lock.Lock()
62+
defer i.lock.Unlock()
63+
64+
metrics.NewPushDescription(descs).Push(i.deploymentsMetric, i.deploymentMetricsMembersMetric, i.deploymentAgencyStateMetric, i.deploymentShardLeadersMetric, i.deploymentShardsMetric)
65+
}
66+
67+
func (i *inventory) Collect(m chan<- prometheus.Metric) {
68+
i.lock.Lock()
69+
defer i.lock.Unlock()
70+
71+
p := metrics.NewPushMetric(m)
72+
for _, deployments := range i.deployments {
73+
for _, deployment := range deployments {
74+
p.Push(i.deploymentsMetric.Gauge(1, deployment.GetNamespace(), deployment.GetName()))
75+
76+
spec := deployment.GetSpec()
77+
status, _ := deployment.GetStatus()
78+
79+
for _, member := range status.Members.AsList() {
80+
p.Push(i.deploymentMetricsMembersMetric.Gauge(1, deployment.GetNamespace(), deployment.GetName(), member.Group.AsRole(), member.Member.ID))
81+
}
82+
83+
if spec.Mode.Get().HasAgents() {
84+
agency, agencyOk := deployment.GetAgencyCache()
85+
if !agencyOk {
86+
p.Push(i.deploymentAgencyStateMetric.Gauge(0, deployment.GetNamespace(), deployment.GetName()))
87+
continue
88+
}
89+
90+
p.Push(i.deploymentAgencyStateMetric.Gauge(1, deployment.GetNamespace(), deployment.GetName()))
91+
92+
if spec.Mode.Get() == api.DeploymentModeCluster {
93+
for db, collections := range agency.Current.Collections {
94+
for collection, shards := range collections {
95+
for shard, details := range shards {
96+
for id, server := range details.Servers {
97+
name := "UNKNOWN"
98+
if _, ok := agency.Plan.Collections[db]; ok {
99+
if _, ok := agency.Plan.Collections[db][collection]; ok {
100+
name = agency.Plan.Collections[db][collection].GetName(name)
101+
}
102+
}
103+
104+
m := []string{
105+
deployment.GetNamespace(),
106+
deployment.GetName(),
107+
db,
108+
name,
109+
shard,
110+
server,
111+
}
112+
113+
if id == 0 {
114+
p.Push(i.deploymentShardLeadersMetric.Gauge(1, m...))
115+
}
116+
p.Push(i.deploymentShardsMetric.Gauge(1, m...))
117+
}
118+
}
119+
}
120+
}
121+
}
122+
}
123+
}
124+
}
125+
}
126+
127+
func (i *inventory) Add(d *Deployment) {
128+
i.lock.Lock()
129+
defer i.lock.Unlock()
130+
131+
name, namespace := d.GetName(), d.GetNamespace()
132+
133+
if _, ok := i.deployments[namespace]; !ok {
134+
i.deployments[namespace] = map[string]*Deployment{}
135+
}
136+
137+
i.deployments[namespace][name] = d
138+
}

pkg/util/metrics/collector.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2016-2021 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package metrics
22+
23+
import "github.com/prometheus/client_golang/prometheus"
24+
25+
type Collector interface {
26+
Collect(description Description, metrics chan<- prometheus.Metric)
27+
}

pkg/util/metrics/description.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2016-2021 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package metrics
22+
23+
import (
24+
"github.com/arangodb/kube-arangodb/pkg/util"
25+
"github.com/prometheus/client_golang/prometheus"
26+
dto "github.com/prometheus/client_model/go"
27+
)
28+
29+
type Description interface {
30+
Desc() *prometheus.Desc
31+
Labels(labels ...string) []*dto.LabelPair
32+
33+
Collect(out chan<- prometheus.Metric, collect func(p Producer) error)
34+
35+
Gauge(value float64, labels ...string) Metric
36+
}
37+
38+
type description struct {
39+
variableLabels []string
40+
constLabels prometheus.Labels
41+
desc *prometheus.Desc
42+
}
43+
44+
func (d *description) Gauge(value float64, labels ...string) Metric {
45+
return newGauge(d, value, labels...)
46+
}
47+
48+
func (d *description) Collect(out chan<- prometheus.Metric, collect func(p Producer) error) {
49+
if err := collect(newProducer(out, d)); err != nil {
50+
out <- newError(d, err)
51+
}
52+
}
53+
54+
func (d *description) Desc() *prometheus.Desc {
55+
return d.desc
56+
}
57+
58+
func (d *description) Labels(labels ...string) []*dto.LabelPair {
59+
var l []*dto.LabelPair
60+
61+
for k, v := range d.constLabels {
62+
var z dto.LabelPair
63+
64+
z.Name = util.NewString(k)
65+
z.Value = util.NewString(v)
66+
l = append(l, &z)
67+
}
68+
69+
for id := range labels {
70+
if id >= len(d.variableLabels) {
71+
break
72+
}
73+
74+
var z dto.LabelPair
75+
76+
z.Name = util.NewString(d.variableLabels[id])
77+
z.Value = util.NewString(labels[id])
78+
l = append(l, &z)
79+
}
80+
81+
return l
82+
}
83+
84+
func NewDescription(fqName, help string, variableLabels []string, constLabels prometheus.Labels) Description {
85+
return &description{
86+
variableLabels: variableLabels,
87+
constLabels: constLabels,
88+
desc: prometheus.NewDesc(fqName, help, variableLabels, constLabels),
89+
}
90+
}

pkg/util/metrics/metric.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2016-2021 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package metrics
22+
23+
import (
24+
"github.com/prometheus/client_golang/prometheus"
25+
dto "github.com/prometheus/client_model/go"
26+
)
27+
28+
type Metric interface {
29+
prometheus.Metric
30+
}
31+
32+
type Gauge interface {
33+
Metric
34+
}
35+
36+
type Error interface {
37+
Metric
38+
}
39+
40+
type errorRet struct {
41+
desc Description
42+
err error
43+
}
44+
45+
func (e errorRet) Desc() *prometheus.Desc {
46+
return e.desc.Desc()
47+
}
48+
49+
func (e errorRet) Write(metric *dto.Metric) error {
50+
return e.err
51+
}
52+
53+
func newError(desc Description, value error) Error {
54+
return errorRet{
55+
desc: desc,
56+
err: value,
57+
}
58+
}
59+
60+
func newGauge(desc Description, value float64, labels ...string) Gauge {
61+
return gauge{
62+
desc: desc,
63+
labels: labels,
64+
value: value,
65+
}
66+
}
67+
68+
type gauge struct {
69+
desc Description
70+
71+
labels []string
72+
73+
value float64
74+
}
75+
76+
func (g gauge) Desc() *prometheus.Desc {
77+
return g.desc.Desc()
78+
}
79+
80+
func (g gauge) Write(metric *dto.Metric) error {
81+
metric.Label = g.desc.Labels(g.labels...)
82+
83+
metric.Gauge = &dto.Gauge{
84+
Value: &g.value,
85+
}
86+
87+
return nil
88+
}

0 commit comments

Comments
 (0)