Skip to content

Commit f8fda06

Browse files
committed
[metrics] Dependency resolution metrics
This PR introduces two histogram metrics to help in analysing the time taken by dependency resolution requests.
1 parent afd2348 commit f8fda06

File tree

6 files changed

+147
-2
lines changed

6 files changed

+147
-2
lines changed

pkg/controller/operators/catalog/operator.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clo
128128
// Create an OperatorLister
129129
lister := operatorlister.NewLister()
130130

131+
operatorV1alpha1Resolver := resolver.NewOperatorsV1alpha1Resolver(lister, crClient, opClient.KubernetesInterface())
132+
successMetricsEmitter := metrics.RegisterDependencyResolutionSuccess
133+
failureMetricsEmitter := metrics.RegisterDependencyResolutionFailure
131134
// Allocate the new instance of an Operator.
132135
op := &Operator{
133136
Operator: queueOperator,
@@ -138,7 +141,7 @@ func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clo
138141
client: crClient,
139142
lister: lister,
140143
namespace: operatorNamespace,
141-
resolver: resolver.NewOperatorsV1alpha1Resolver(lister, crClient, opClient.KubernetesInterface()),
144+
resolver: resolver.NewInstrumentedResolver(operatorV1alpha1Resolver, successMetricsEmitter, failureMetricsEmitter),
142145
catsrcQueueSet: queueinformer.NewEmptyResourceQueueSet(),
143146
subQueueSet: queueinformer.NewEmptyResourceQueueSet(),
144147
ipQueueSet: queueinformer.NewEmptyResourceQueueSet(),
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package resolver
2+
3+
import (
4+
"time"
5+
6+
"github.com/operator-framework/api/pkg/operators/v1alpha1"
7+
)
8+
9+
type InstrumentedResolver struct {
10+
operatorsV1alpha1Resolver Resolver
11+
successMetricsEmitter func(*float64)
12+
failureMetricsEmitter func(*float64)
13+
}
14+
15+
var _ Resolver = &OperatorsV1alpha1Resolver{}
16+
17+
func NewInstrumentedResolver(resolver Resolver, successMetricsEmitter, failureMetricsEmitter func(*float64)) *InstrumentedResolver {
18+
return &InstrumentedResolver{
19+
operatorsV1alpha1Resolver: resolver,
20+
successMetricsEmitter: successMetricsEmitter,
21+
failureMetricsEmitter: failureMetricsEmitter,
22+
}
23+
}
24+
25+
func (ir *InstrumentedResolver) ResolveSteps(namespace string, sourceQuerier SourceQuerier) ([]*v1alpha1.Step, []v1alpha1.BundleLookup, []*v1alpha1.Subscription, error) {
26+
start := time.Now()
27+
steps, lookups, subs, err := ir.operatorsV1alpha1Resolver.ResolveSteps(namespace, sourceQuerier)
28+
elapsed := time.Now().Sub(start)
29+
elapsedInSeconds := elapsed.Seconds()
30+
if err != nil {
31+
ir.failureMetricsEmitter(&elapsedInSeconds)
32+
} else {
33+
ir.successMetricsEmitter(&elapsedInSeconds)
34+
}
35+
return steps, lookups, subs, err
36+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package resolver
2+
3+
import (
4+
"errors"
5+
"testing"
6+
7+
"github.com/operator-framework/api/pkg/operators/v1alpha1"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
const (
12+
failure = 0.0
13+
success = 1.0
14+
reset = 999.99
15+
)
16+
17+
type fakeResolverWithError struct{}
18+
type fakeResolverWithoutError struct{}
19+
20+
func (r *fakeResolverWithError) ResolveSteps(namespace string, sourceQuerier SourceQuerier) ([]*v1alpha1.Step, []v1alpha1.BundleLookup, []*v1alpha1.Subscription, error) {
21+
return nil, nil, nil, errors.New("Fake error")
22+
}
23+
24+
func (r *fakeResolverWithoutError) ResolveSteps(namespace string, sourceQuerier SourceQuerier) ([]*v1alpha1.Step, []v1alpha1.BundleLookup, []*v1alpha1.Subscription, error) {
25+
return nil, nil, nil, nil
26+
}
27+
28+
func newFakeResolverWithError() *fakeResolverWithError {
29+
return &fakeResolverWithError{}
30+
}
31+
32+
func newFakeResolverWithoutError() *fakeResolverWithoutError {
33+
return &fakeResolverWithoutError{}
34+
}
35+
36+
func TestInstrumentedResolver(t *testing.T) {
37+
38+
var result float64
39+
40+
changeToFailure := func(num *float64) {
41+
*num = failure
42+
result = *num
43+
}
44+
45+
changeToSuccess := func(num *float64) {
46+
*num = success
47+
result = *num
48+
}
49+
50+
result = reset
51+
instrumentedResolver := NewInstrumentedResolver(newFakeResolverWithError(), changeToSuccess, changeToFailure)
52+
instrumentedResolver.ResolveSteps("", nil)
53+
require.Equal(t, result, failure)
54+
55+
result = reset
56+
instrumentedResolver = NewInstrumentedResolver(newFakeResolverWithoutError(), changeToSuccess, changeToFailure)
57+
instrumentedResolver.ResolveSteps("", nil)
58+
require.Equal(t, result, success)
59+
}

pkg/metrics/metrics.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,21 @@ var (
170170
[]string{NAMESPACE_LABEL, NAME_LABEL, VERSION_LABEL, PHASE_LABEL, REASON_LABEL},
171171
)
172172

173+
dependencyResolutionSucceeded = prometheus.NewSummary(
174+
prometheus.SummaryOpts{
175+
Name: "dependency_resolution_succeeded",
176+
Help: "The duration for a dependency to get resolved successfully",
177+
Objectives: map[float64]float64{0.95: 0.05, 0.9: 0.01, 0.99: 0.001},
178+
},
179+
)
180+
181+
dependencyResolutionFailed = prometheus.NewSummary(
182+
prometheus.SummaryOpts{
183+
Name: "dependency_resolution_failed",
184+
Help: "The duration of an unsuccessful dependency resolution",
185+
Objectives: map[float64]float64{0.95: 0.05, 0.9: 0.01, 0.99: 0.001},
186+
},
187+
)
173188
// subscriptionSyncCounters keeps a record of the promethues counters emitted by
174189
// Subscription objects. The key of a record is the Subscription name, while the value
175190
// is struct containing label values used in the counter
@@ -194,6 +209,8 @@ func RegisterCatalog() {
194209
prometheus.MustRegister(subscriptionCount)
195210
prometheus.MustRegister(catalogSourceCount)
196211
prometheus.MustRegister(SubscriptionSyncCount)
212+
prometheus.MustRegister(dependencyResolutionSucceeded)
213+
prometheus.MustRegister(dependencyResolutionFailed)
197214
}
198215

199216
func CounterForSubscription(name, installedCSV, channelName, packageName string) prometheus.Counter {
@@ -269,3 +286,11 @@ func UpdateSubsSyncCounterStorage(sub *olmv1alpha1.Subscription) {
269286
counterValues.channel = sub.Spec.Channel
270287
}
271288
}
289+
290+
func RegisterDependencyResolutionSuccess(time *float64) {
291+
dependencyResolutionSucceeded.Observe(*time)
292+
}
293+
294+
func RegisterDependencyResolutionFailure(time *float64) {
295+
dependencyResolutionFailed.Observe(*time)
296+
}

test/e2e/like_metric_matcher_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,15 @@ func WithValue(v float64) MetricPredicate {
5656
}
5757
}
5858

59+
func WithValueGreaterThan(v float64) MetricPredicate {
60+
return MetricPredicate{
61+
name: fmt.Sprintf("WithValueGreaterThan(%g)", v),
62+
f: func(m Metric) bool {
63+
return m.Value > v
64+
},
65+
}
66+
}
67+
5968
type LikeMetricMatcher struct {
6069
Predicates []MetricPredicate
6170
}

test/e2e/metrics_e2e_test.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ var _ = Describe("Metrics are generated for OLM managed resources", func() {
109109
})
110110
})
111111

112-
Context("Subscription Metric", func() {
112+
Context("Metrics emitted by objects during operator installation", func() {
113113
var (
114114
subscriptionCleanup cleanupFunc
115115
subscription *v1alpha1.Subscription
@@ -138,6 +138,19 @@ var _ = Describe("Metrics are generated for OLM managed resources", func() {
138138
WithLabel("package", testPackageName),
139139
)))
140140
})
141+
142+
It("generates dependency_resolution metric", func() {
143+
144+
// Verify metrics have been emitted for dependency resolution
145+
Eventually(func() bool {
146+
return Eventually(func() []Metric {
147+
return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator"), "8081")
148+
}).Should(ContainElement(LikeMetric(
149+
WithFamily("dependency_resolution_failed"),
150+
WithValueGreaterThan(0),
151+
)))
152+
})
153+
})
141154
})
142155

143156
When("A subscription object is updated after emitting metrics", func() {

0 commit comments

Comments
 (0)