Skip to content

The method io.kubernetes.client.informer.SharedInformerFactory#sharedIndexInformerFor should ideally be idempotent. #3849

@Sud0x67

Description

@Sud0x67

Describe the bug
In our service, we utilize the SharedInformerFactory to generate informers. However, we encountered unexpected behavior when invoking it multiple times. Specifically, it returns a new informer for the same apiTypeClass without maintaining it within the internal map named informers. Consequently, when we call the io.kubernetes.client.informer.SharedInformerFactory#startAllRegisteredInformers method, our informer doesn't start as anticipated. The code is as follows:

  public synchronized <ApiType extends KubernetesObject, ApiListType extends KubernetesListObject>
      SharedIndexInformer<ApiType> sharedIndexInformerFor(
          ListerWatcher<ApiType, ApiListType> listerWatcher,
          Class<ApiType> apiTypeClass,
          long resyncPeriodInMillis,
          BiConsumer<Class<ApiType>, Throwable> exceptionHandler) {

    SharedIndexInformer<ApiType> informer =
        new DefaultSharedIndexInformer<>(
            apiTypeClass, listerWatcher, resyncPeriodInMillis, new Cache<>(), exceptionHandler);
    this.informers.putIfAbsent(TypeToken.get(apiTypeClass).getType(), informer);
    return informer;
  }

This is a bit confusing. I don't understand why the factory created it but doesn't keep it. Could someone explain the purpose of this design to me?

Describe the solution you'd like

I believe the factory should retain all the informers it creates, ensuring that when we call startAllRegisteredInformers, the returned informer starts correctly. Alternatively, the method SharedInformerFactory#sharedIndexInformerFor should be idempotent. This means that if we create an informer for the same apiClassType, it should return the initial instance, similar to the behavior observed in the Go client.

func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer {
	f.lock.Lock()
	defer f.lock.Unlock()

	informerType := reflect.TypeOf(obj)
	informer, exists := f.informers[informerType]
	if exists {
		return informer
	}

	resyncPeriod, exists := f.customResync[informerType]
	if !exists {
		resyncPeriod = f.defaultResync
	}

	informer = newFunc(f.client, resyncPeriod)
	informer.SetTransform(f.transform)
	f.informers[informerType] = informer

	return informer
}

No response

Client Version
e.g. 8.0.2

Kubernetes Version
e.g. 1.19.3

Java Version
e.g. Java11

Metadata

Metadata

Assignees

No one assigned

    Labels

    lifecycle/rottenDenotes an issue or PR that has aged beyond stale and will be auto-closed.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions