k8s.io/apiserver@v0.31.1/pkg/storage/cacher/caching_object.go (about)

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package cacher
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"io"
    23  	"reflect"
    24  	"runtime/debug"
    25  	"sync"
    26  	"sync/atomic"
    27  	"time"
    28  
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/runtime"
    31  	"k8s.io/apimachinery/pkg/runtime/schema"
    32  	"k8s.io/apimachinery/pkg/types"
    33  	"k8s.io/klog/v2"
    34  )
    35  
    36  var _ runtime.CacheableObject = &cachingObject{}
    37  
    38  // metaRuntimeInterface implements runtime.Object and
    39  // metav1.Object interfaces.
    40  type metaRuntimeInterface interface {
    41  	runtime.Object
    42  	metav1.Object
    43  }
    44  
    45  // serializationResult captures a result of serialization.
    46  type serializationResult struct {
    47  	// once should be used to ensure serialization is computed once.
    48  	once sync.Once
    49  
    50  	// raw is serialized object.
    51  	raw []byte
    52  	// err is error from serialization.
    53  	err error
    54  }
    55  
    56  // serializationsCache is a type for caching serialization results.
    57  type serializationsCache map[runtime.Identifier]*serializationResult
    58  
    59  // cachingObject is an object that is able to cache its serializations
    60  // so that each of those is computed exactly once.
    61  //
    62  // cachingObject implements the metav1.Object interface (accessors for
    63  // all metadata fields).
    64  type cachingObject struct {
    65  	lock sync.RWMutex
    66  
    67  	// deepCopied defines whether the object below has already been
    68  	// deep copied. The operation is performed lazily on the first
    69  	// setXxx operation.
    70  	//
    71  	// The lazy deep-copy make is useful, as effectively the only
    72  	// case when we are setting some fields are ResourceVersion for
    73  	// DELETE events, so in all other cases we can effectively avoid
    74  	// performing any deep copies.
    75  	deepCopied bool
    76  
    77  	// Object for which serializations are cached.
    78  	object metaRuntimeInterface
    79  
    80  	// serializations is a cache containing object`s serializations.
    81  	// The value stored in atomic.Value is of type serializationsCache.
    82  	// The atomic.Value type is used to allow fast-path.
    83  	serializations atomic.Value
    84  }
    85  
    86  // newCachingObject performs a deep copy of the given object and wraps it
    87  // into a cachingObject.
    88  // An error is returned if it's not possible to cast the object to
    89  // metav1.Object type.
    90  func newCachingObject(object runtime.Object) (*cachingObject, error) {
    91  	if obj, ok := object.(metaRuntimeInterface); ok {
    92  		result := &cachingObject{
    93  			object:     obj,
    94  			deepCopied: false,
    95  		}
    96  		result.serializations.Store(make(serializationsCache))
    97  		return result, nil
    98  	}
    99  	return nil, fmt.Errorf("can't cast object to metav1.Object: %#v", object)
   100  }
   101  
   102  func (o *cachingObject) getSerializationResult(id runtime.Identifier) *serializationResult {
   103  	// Fast-path for getting from cache.
   104  	serializations := o.serializations.Load().(serializationsCache)
   105  	if result, exists := serializations[id]; exists {
   106  		return result
   107  	}
   108  
   109  	// Slow-path (that may require insert).
   110  	o.lock.Lock()
   111  	defer o.lock.Unlock()
   112  
   113  	serializations = o.serializations.Load().(serializationsCache)
   114  	// Check if in the meantime it wasn't inserted.
   115  	if result, exists := serializations[id]; exists {
   116  		return result
   117  	}
   118  
   119  	// Insert an entry for <id>. This requires copy of existing map.
   120  	newSerializations := make(serializationsCache)
   121  	for k, v := range serializations {
   122  		newSerializations[k] = v
   123  	}
   124  	result := &serializationResult{}
   125  	newSerializations[id] = result
   126  	o.serializations.Store(newSerializations)
   127  	return result
   128  }
   129  
   130  // CacheEncode implements runtime.CacheableObject interface.
   131  // It serializes the object and writes the result to given io.Writer trying
   132  // to first use the already cached result and falls back to a given encode
   133  // function in case of cache miss.
   134  // It assumes that for a given identifier, the encode function always encodes
   135  // each input object into the same output format.
   136  func (o *cachingObject) CacheEncode(id runtime.Identifier, encode func(runtime.Object, io.Writer) error, w io.Writer) error {
   137  	result := o.getSerializationResult(id)
   138  	result.once.Do(func() {
   139  		buffer := bytes.NewBuffer(nil)
   140  		// TODO(wojtek-t): This is currently making a copy to avoid races
   141  		//   in cases where encoding is making subtle object modifications,
   142  		//   e.g. #82497
   143  		//   Figure out if we can somehow avoid this under some conditions.
   144  		result.err = encode(o.GetObject(), buffer)
   145  		result.raw = buffer.Bytes()
   146  	})
   147  	// Once invoked, fields of serialization will not change.
   148  	if result.err != nil {
   149  		return result.err
   150  	}
   151  	if b, support := w.(runtime.Splice); support {
   152  		b.Splice(result.raw)
   153  		return nil
   154  	}
   155  	_, err := w.Write(result.raw)
   156  	return err
   157  }
   158  
   159  // GetObject implements runtime.CacheableObject interface.
   160  // It returns deep-copy of the wrapped object to return ownership of it
   161  // to the called according to the contract of the interface.
   162  func (o *cachingObject) GetObject() runtime.Object {
   163  	o.lock.RLock()
   164  	defer o.lock.RUnlock()
   165  	return o.object.DeepCopyObject().(metaRuntimeInterface)
   166  }
   167  
   168  // GetObjectKind implements runtime.Object interface.
   169  func (o *cachingObject) GetObjectKind() schema.ObjectKind {
   170  	o.lock.RLock()
   171  	defer o.lock.RUnlock()
   172  	return o.object.GetObjectKind()
   173  }
   174  
   175  // DeepCopyObject implements runtime.Object interface.
   176  func (o *cachingObject) DeepCopyObject() runtime.Object {
   177  	// DeepCopyObject on cachingObject is not expected to be called anywhere.
   178  	// However, to be on the safe-side, we implement it, though given the
   179  	// cache is only an optimization we ignore copying it.
   180  	result := &cachingObject{
   181  		deepCopied: true,
   182  	}
   183  	result.serializations.Store(make(serializationsCache))
   184  
   185  	o.lock.RLock()
   186  	defer o.lock.RUnlock()
   187  	result.object = o.object.DeepCopyObject().(metaRuntimeInterface)
   188  	return result
   189  }
   190  
   191  var (
   192  	invalidationCacheTimestampLock sync.Mutex
   193  	invalidationCacheTimestamp     time.Time
   194  )
   195  
   196  // shouldLogCacheInvalidation allows for logging cache-invalidation
   197  // at most once per second (to avoid spamming logs in case of issues).
   198  func shouldLogCacheInvalidation(now time.Time) bool {
   199  	invalidationCacheTimestampLock.Lock()
   200  	defer invalidationCacheTimestampLock.Unlock()
   201  	if invalidationCacheTimestamp.Add(time.Second).Before(now) {
   202  		invalidationCacheTimestamp = now
   203  		return true
   204  	}
   205  	return false
   206  }
   207  
   208  func (o *cachingObject) invalidateCacheLocked() {
   209  	if cache, ok := o.serializations.Load().(serializationsCache); ok && len(cache) == 0 {
   210  		return
   211  	}
   212  	// We don't expect cache invalidation to happen - so we want
   213  	// to log the stacktrace to allow debugging if that will happen.
   214  	// OTOH, we don't want to spam logs with it.
   215  	// So we try to log it at most once per second.
   216  	if shouldLogCacheInvalidation(time.Now()) {
   217  		klog.Warningf("Unexpected cache invalidation for %#v\n%s", o.object, string(debug.Stack()))
   218  	}
   219  	o.serializations.Store(make(serializationsCache))
   220  }
   221  
   222  // The following functions implement metav1.Object interface:
   223  // - getters simply delegate for the underlying object
   224  // - setters check if operations isn't noop and if so,
   225  //   invalidate the cache and delegate for the underlying object
   226  
   227  func (o *cachingObject) conditionalSet(isNoop func() bool, set func()) {
   228  	if fastPath := func() bool {
   229  		o.lock.RLock()
   230  		defer o.lock.RUnlock()
   231  		return isNoop()
   232  	}(); fastPath {
   233  		return
   234  	}
   235  	o.lock.Lock()
   236  	defer o.lock.Unlock()
   237  	if isNoop() {
   238  		return
   239  	}
   240  	if !o.deepCopied {
   241  		o.object = o.object.DeepCopyObject().(metaRuntimeInterface)
   242  		o.deepCopied = true
   243  	}
   244  	o.invalidateCacheLocked()
   245  	set()
   246  }
   247  
   248  func (o *cachingObject) GetNamespace() string {
   249  	o.lock.RLock()
   250  	defer o.lock.RUnlock()
   251  	return o.object.GetNamespace()
   252  }
   253  func (o *cachingObject) SetNamespace(namespace string) {
   254  	o.conditionalSet(
   255  		func() bool { return o.object.GetNamespace() == namespace },
   256  		func() { o.object.SetNamespace(namespace) },
   257  	)
   258  }
   259  func (o *cachingObject) GetName() string {
   260  	o.lock.RLock()
   261  	defer o.lock.RUnlock()
   262  	return o.object.GetName()
   263  }
   264  func (o *cachingObject) SetName(name string) {
   265  	o.conditionalSet(
   266  		func() bool { return o.object.GetName() == name },
   267  		func() { o.object.SetName(name) },
   268  	)
   269  }
   270  func (o *cachingObject) GetGenerateName() string {
   271  	o.lock.RLock()
   272  	defer o.lock.RUnlock()
   273  	return o.object.GetGenerateName()
   274  }
   275  func (o *cachingObject) SetGenerateName(name string) {
   276  	o.conditionalSet(
   277  		func() bool { return o.object.GetGenerateName() == name },
   278  		func() { o.object.SetGenerateName(name) },
   279  	)
   280  }
   281  func (o *cachingObject) GetUID() types.UID {
   282  	o.lock.RLock()
   283  	defer o.lock.RUnlock()
   284  	return o.object.GetUID()
   285  }
   286  func (o *cachingObject) SetUID(uid types.UID) {
   287  	o.conditionalSet(
   288  		func() bool { return o.object.GetUID() == uid },
   289  		func() { o.object.SetUID(uid) },
   290  	)
   291  }
   292  func (o *cachingObject) GetResourceVersion() string {
   293  	o.lock.RLock()
   294  	defer o.lock.RUnlock()
   295  	return o.object.GetResourceVersion()
   296  }
   297  func (o *cachingObject) SetResourceVersion(version string) {
   298  	o.conditionalSet(
   299  		func() bool { return o.object.GetResourceVersion() == version },
   300  		func() { o.object.SetResourceVersion(version) },
   301  	)
   302  }
   303  func (o *cachingObject) GetGeneration() int64 {
   304  	o.lock.RLock()
   305  	defer o.lock.RUnlock()
   306  	return o.object.GetGeneration()
   307  }
   308  func (o *cachingObject) SetGeneration(generation int64) {
   309  	o.conditionalSet(
   310  		func() bool { return o.object.GetGeneration() == generation },
   311  		func() { o.object.SetGeneration(generation) },
   312  	)
   313  }
   314  func (o *cachingObject) GetSelfLink() string {
   315  	o.lock.RLock()
   316  	defer o.lock.RUnlock()
   317  	return o.object.GetSelfLink()
   318  }
   319  func (o *cachingObject) SetSelfLink(selfLink string) {
   320  	o.conditionalSet(
   321  		func() bool { return o.object.GetSelfLink() == selfLink },
   322  		func() { o.object.SetSelfLink(selfLink) },
   323  	)
   324  }
   325  func (o *cachingObject) GetCreationTimestamp() metav1.Time {
   326  	o.lock.RLock()
   327  	defer o.lock.RUnlock()
   328  	return o.object.GetCreationTimestamp()
   329  }
   330  func (o *cachingObject) SetCreationTimestamp(timestamp metav1.Time) {
   331  	o.conditionalSet(
   332  		func() bool { return o.object.GetCreationTimestamp() == timestamp },
   333  		func() { o.object.SetCreationTimestamp(timestamp) },
   334  	)
   335  }
   336  func (o *cachingObject) GetDeletionTimestamp() *metav1.Time {
   337  	o.lock.RLock()
   338  	defer o.lock.RUnlock()
   339  	return o.object.GetDeletionTimestamp()
   340  }
   341  func (o *cachingObject) SetDeletionTimestamp(timestamp *metav1.Time) {
   342  	o.conditionalSet(
   343  		func() bool { return o.object.GetDeletionTimestamp() == timestamp },
   344  		func() { o.object.SetDeletionTimestamp(timestamp) },
   345  	)
   346  }
   347  func (o *cachingObject) GetDeletionGracePeriodSeconds() *int64 {
   348  	o.lock.RLock()
   349  	defer o.lock.RUnlock()
   350  	return o.object.GetDeletionGracePeriodSeconds()
   351  }
   352  func (o *cachingObject) SetDeletionGracePeriodSeconds(gracePeriodSeconds *int64) {
   353  	o.conditionalSet(
   354  		func() bool { return o.object.GetDeletionGracePeriodSeconds() == gracePeriodSeconds },
   355  		func() { o.object.SetDeletionGracePeriodSeconds(gracePeriodSeconds) },
   356  	)
   357  }
   358  func (o *cachingObject) GetLabels() map[string]string {
   359  	o.lock.RLock()
   360  	defer o.lock.RUnlock()
   361  	return o.object.GetLabels()
   362  }
   363  func (o *cachingObject) SetLabels(labels map[string]string) {
   364  	o.conditionalSet(
   365  		func() bool { return reflect.DeepEqual(o.object.GetLabels(), labels) },
   366  		func() { o.object.SetLabels(labels) },
   367  	)
   368  }
   369  func (o *cachingObject) GetAnnotations() map[string]string {
   370  	o.lock.RLock()
   371  	defer o.lock.RUnlock()
   372  	return o.object.GetAnnotations()
   373  }
   374  func (o *cachingObject) SetAnnotations(annotations map[string]string) {
   375  	o.conditionalSet(
   376  		func() bool { return reflect.DeepEqual(o.object.GetAnnotations(), annotations) },
   377  		func() { o.object.SetAnnotations(annotations) },
   378  	)
   379  }
   380  func (o *cachingObject) GetFinalizers() []string {
   381  	o.lock.RLock()
   382  	defer o.lock.RUnlock()
   383  	return o.object.GetFinalizers()
   384  }
   385  func (o *cachingObject) SetFinalizers(finalizers []string) {
   386  	o.conditionalSet(
   387  		func() bool { return reflect.DeepEqual(o.object.GetFinalizers(), finalizers) },
   388  		func() { o.object.SetFinalizers(finalizers) },
   389  	)
   390  }
   391  func (o *cachingObject) GetOwnerReferences() []metav1.OwnerReference {
   392  	o.lock.RLock()
   393  	defer o.lock.RUnlock()
   394  	return o.object.GetOwnerReferences()
   395  }
   396  func (o *cachingObject) SetOwnerReferences(references []metav1.OwnerReference) {
   397  	o.conditionalSet(
   398  		func() bool { return reflect.DeepEqual(o.object.GetOwnerReferences(), references) },
   399  		func() { o.object.SetOwnerReferences(references) },
   400  	)
   401  }
   402  func (o *cachingObject) GetManagedFields() []metav1.ManagedFieldsEntry {
   403  	o.lock.RLock()
   404  	defer o.lock.RUnlock()
   405  	return o.object.GetManagedFields()
   406  }
   407  func (o *cachingObject) SetManagedFields(managedFields []metav1.ManagedFieldsEntry) {
   408  	o.conditionalSet(
   409  		func() bool { return reflect.DeepEqual(o.object.GetManagedFields(), managedFields) },
   410  		func() { o.object.SetManagedFields(managedFields) },
   411  	)
   412  }