benchmark and metrics for new timestamp transformer comparison

add proper metrics

rename & improve documentation for path metric dimension

Kubernetes-commit: 40343793f7b9787b2d4b88f0a0439ce9e538075a
This commit is contained in:
Alexander Zielenski 2022-03-01 14:24:12 -08:00 committed by Kubernetes Publisher
parent 81749c6ee9
commit 48eb70e1d1
2 changed files with 67 additions and 3 deletions

View File

@ -20,12 +20,14 @@ import (
"context"
"fmt"
"reflect"
"time"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/endpoints/metrics"
)
var ignoreTimestampEqualities = func() conversion.Equalities {
@ -54,7 +56,20 @@ func IgnoreManagedFieldsTimestampsTransformer(
_ context.Context,
newObj runtime.Object,
oldObj runtime.Object,
) (runtime.Object, error) {
) (res runtime.Object, err error) {
outcome := "unequal_objects_fast"
start := time.Now()
err = nil
res = nil
defer func() {
if err != nil {
outcome = "error"
}
metrics.RecordTimestampComparisonLatency(outcome, time.Since(start))
}()
// If managedFields modulo timestamps are unchanged
// and
// rest of object is unchanged
@ -85,12 +100,41 @@ func IgnoreManagedFieldsTimestampsTransformer(
oldManagedFields := oldAccessor.GetManagedFields()
newManagedFields := accessor.GetManagedFields()
if len(oldManagedFields) != len(newManagedFields) {
// Return early if any managed fields entry was added/removed.
// We want to retain user expectation that even if they write to a field
// whose value did not change, they will still result as the field
// manager at the end.
return newObj, nil
} else if len(newManagedFields) == 0 {
// This transformation only makes sense when managedFields are
// non-empty
return newObj, nil
}
// This transformation only makes sense if the managed fields has at least one
// changed timestamp; and are otherwise equal. Return early if there are no
// changed timestamps.
allTimesUnchanged := true
for i, e := range newManagedFields {
if !e.Time.Equal(oldManagedFields[i].Time) {
allTimesUnchanged = false
break
}
}
if allTimesUnchanged {
return newObj, nil
}
// This condition ensures the managed fields are always compared first. If
// this check fails, the if statement will short circuit. If the check
// succeeds the slow path is taken which compares entire objects.
if ignoreTimestampEqualities.DeepEqualWithNilDifferentFromEmpty(oldManagedFields, newManagedFields) &&
ignoreTimestampEqualities.DeepEqualWithNilDifferentFromEmpty(newObj, oldObj) {
if !ignoreTimestampEqualities.DeepEqualWithNilDifferentFromEmpty(oldManagedFields, newManagedFields) {
return newObj, nil
}
if ignoreTimestampEqualities.DeepEqualWithNilDifferentFromEmpty(newObj, oldObj) {
// Remove any changed timestamps, so that timestamp is not the only
// change seen by etcd.
//
@ -103,7 +147,10 @@ func IgnoreManagedFieldsTimestampsTransformer(
}
accessor.SetManagedFields(newManagedFields)
outcome = "equal_objects"
return newObj, nil
}
outcome = "unequal_objects_slow"
return newObj, nil
}

View File

@ -238,6 +238,18 @@ var (
[]string{"source", "status"},
)
requestTimestampComparisonDuration = compbasemetrics.NewHistogramVec(
&compbasemetrics.HistogramOpts{
Name: "apiserver_request_timestamp_comparison_time",
Help: "Time taken for comparison of old vs new objects in UPDATE or PATCH requests",
Buckets: []float64{0.0001, 0.0003, 0.001, 0.003, 0.01, 0.03, 0.1, 0.3, 1.0, 5.0},
StabilityLevel: compbasemetrics.ALPHA,
},
// Path the code takes to reach a conclusion:
// i.e. unequalObjectsFast, unequalObjectsSlow, equalObjectsSlow
[]string{"code_path"},
)
metrics = []resettableCollector{
deprecatedRequestGauge,
requestCounter,
@ -256,6 +268,7 @@ var (
requestFilterDuration,
requestAbortsTotal,
requestPostTimeoutTotal,
requestTimestampComparisonDuration,
}
// these are the valid request methods which we report in our metrics. Any other request methods
@ -366,6 +379,10 @@ func RecordFilterLatency(ctx context.Context, name string, elapsed time.Duration
requestFilterDuration.WithContext(ctx).WithLabelValues(name).Observe(elapsed.Seconds())
}
func RecordTimestampComparisonLatency(codePath string, elapsed time.Duration) {
requestTimestampComparisonDuration.WithLabelValues(codePath).Observe(elapsed.Seconds())
}
func RecordRequestPostTimeout(source string, status string) {
requestPostTimeoutTotal.WithLabelValues(source, status).Inc()
}