diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanager.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanager.go index 95742bf9d57..262d5d7a829 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanager.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanager.go @@ -33,6 +33,10 @@ import ( // TODO(jennybuckley): Determine if this is really the best value. Ideally we wouldn't unnecessarily merge too many entries. const DefaultMaxUpdateManagers int = 10 +// DefaultTrackOnCreateProbability defines the default probability that the field management of an object +// starts being tracked from the object's creation, instead of from the first time the object is applied to. +const DefaultTrackOnCreateProbability float32 = 0.5 + // Managed groups a fieldpath.ManagedFields together with the timestamps associated with each operation. type Managed interface { // Fields gets the fieldpath.ManagedFields. @@ -92,7 +96,7 @@ func newDefaultFieldManager(f Manager, objectCreater runtime.ObjectCreater, kind f = NewStripMetaManager(f) f = NewBuildManagerInfoManager(f, kind.GroupVersion()) f = NewCapManagersManager(f, DefaultMaxUpdateManagers) - f = NewSkipNonAppliedManager(f, objectCreater, kind) + f = NewProbabilisticSkipNonAppliedManager(f, objectCreater, kind, DefaultTrackOnCreateProbability) return NewFieldManager(f) } diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/skipnonapplied.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/skipnonapplied.go index 913615a6613..a8c34ad6529 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/skipnonapplied.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/skipnonapplied.go @@ -18,7 +18,9 @@ package fieldmanager import ( "fmt" + "math/rand" + "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" ) @@ -28,25 +30,48 @@ type skipNonAppliedManager struct { objectCreater runtime.ObjectCreater gvk schema.GroupVersionKind beforeApplyManagerName string + probability float32 } var _ Manager = &skipNonAppliedManager{} // NewSkipNonAppliedManager creates a new wrapped FieldManager that only starts tracking managers after the first apply. func NewSkipNonAppliedManager(fieldManager Manager, objectCreater runtime.ObjectCreater, gvk schema.GroupVersionKind) Manager { + return NewProbabilisticSkipNonAppliedManager(fieldManager, objectCreater, gvk, 0.0) +} + +// NewProbabilisticSkipNonAppliedManager creates a new wrapped FieldManager that starts tracking managers after the first apply, +// or starts tracking on create with p probability. +func NewProbabilisticSkipNonAppliedManager(fieldManager Manager, objectCreater runtime.ObjectCreater, gvk schema.GroupVersionKind, p float32) Manager { return &skipNonAppliedManager{ fieldManager: fieldManager, objectCreater: objectCreater, gvk: gvk, beforeApplyManagerName: "before-first-apply", + probability: p, } } // Update implements Manager. func (f *skipNonAppliedManager) Update(liveObj, newObj runtime.Object, managed Managed, manager string) (runtime.Object, Managed, error) { - if len(managed.Fields()) == 0 { + accessor, err := meta.Accessor(liveObj) + if err != nil { return newObj, managed, nil } + + // If managed fields is empty, we need to determine whether to skip tracking managed fields. + if len(managed.Fields()) == 0 { + // Check if the operation is a create, by checking whether lastObj's UID is empty. + // If the operation is create, P(tracking managed fields) = f.probability + // If the operation is update, skip tracking managed fields, since we already know managed fields is empty. + if len(accessor.GetUID()) == 0 { + if f.probability <= rand.Float32() { + return newObj, managed, nil + } + } else { + return newObj, managed, nil + } + } return f.fieldManager.Update(liveObj, newObj, managed, manager) }