Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot get rid of Informer sending events #956

Closed
stockersky opened this issue Jan 4, 2023 · 2 comments
Closed

Cannot get rid of Informer sending events #956

stockersky opened this issue Jan 4, 2023 · 2 comments

Comments

@stockersky
Copy link

stockersky commented Jan 4, 2023

Describe the bug
I am developing an ElectronJS application to display Kubernetes objects status (I know such app already exists).
I have problems with the Informer object.
Basically, each view of the app will display & watch K8s objects, depending of the namespace/context.
So, when the user navigate to a new view, I should kill the current Informer and then start a new one, watching a new objects k kinds, possibly in another K8s context.

On "server side" logs, I do not see a bunch of Informers sending events.
However, on the client side (the view), the more I navigate back and forth to the same view, it looks like events are accumulating over and over.

** Client Version **
0.17.1

** Server Version **
Client Version: v1.24.4",
Server Version: v1.24.4+k3s1"

To Reproduce
Here is a snippet :

NOTE : not very elegant, but i reference the Informer in a global object to keep track of it.
I confirm that when quitting the view, the Informer throw an exception ( caught in informer.on('error')) and get ECONNRESET.

  // Object to track state of pods informer
  // Informer should be stopped when we quit the view
  let informerPods = {
    informer: '',
    created: false,
  }
  /* 
  Get all pods from namespace
  when client call this IPC Channel, trigger the creation of an Informer & start it.
  If Informer already exists : stop it.
   ==> this IPC Channel can be called:
          - at vue creation (vuejs "mount") -> create the infromer cause it does not exists
          - when quitting the vue (vuejs "destroy") --> stop the informer cause it already exists
  */
  ipcMain.on('pods-from-namespace', (event, contextname) => {
    if (informerPods.created)  {
      console.log("Pods informer already exist : killing");
      informerPods.informer.stop();
      informerPods = {
        informer: '',
        created: false,
      }
    } else {
      try {
        console.log("Creating Pods informer"); 
        const kc = new k8s.KubeConfig();
        kc.loadFromDefault();

        // IMPORTANT : allow to take into account the context change !
        kc.setCurrentContext(contextname);
        const context = kc.getContextObject(contextname);
        console.log("Active context : " + context.name);
        const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
        
        const listPods = () => k8sApi.listNamespacedPod(context.namespace);
        const informer = k8s.makeInformer(kc, '/api/v1/namespaces/default/pods', listPods);
     
        // update global vers to track Informer
        informerPods = {
          informer: informer,
          created: true,
        }
        
        // ----- Informer events -----
        informer.on('add', (obj) => {
          console.log(`Added: ${obj.metadata.name}`);
          // send message to the view:
          win.webContents.send('pods-from-namespace', "add", obj);
        });
        informer.on('delete', (obj) => {
          console.log(`Deleted: ${obj.metadata.name}`);
          // send message to the view:
          win.webContents.send('pods-from-namespace', "delete", obj);
        });
        informer.on('error', (err) => {
          console.log("Informer ERROR");
          console.error(err);
          informer.stop();
        });
        informer.start();
      } catch(err) {
        console.error("BING");
        console.error(err);
        informer.off();
        informer.stop();
      }
    }
  });

Expected behavior
This is a screenshot of a view watching Pods through Informer. Fresh view just after starting the app :

podview1

So far, it's good. Note : there is indeed 7 pods.

However after navigating back & forth to this view several times, events seems to accumulate... But finally, it render as expected.
There are lots of multiples of 7. This show the accumulation : 7, 14, 21, 28, etc.....

)odview2

Environment (please complete the following information):

  • OS: [MAC & Windows]
  • NodeJS Version : v16.19.0
  • Cloud runtime : Azure AKS

** app stack**

  • ElectronJS + Vuejs2 + Vuetify

** Related Issues**

Those 2 issues are maybe related:
606
604

@brendandburns
Copy link
Contributor

It's hard to know what is going on here given the broader nature of your application. Can you distill this down to a simple reproducible example of the error?

One thing that probably makes sense to do is to always check if informerPods.informer is defined, and if it is defined stop it before you place a new informer on top of it. My suspicion is that somehow the create and over-write of the informerPods.informer variable is happening before the stop event is called and thus the existing informer is getting leaked, but I'm not certain. Regardless it is simpler logic to stop any existing informer before you overwrite it, rather than relying on it having been stopped by a different event.

@stockersky
Copy link
Author

Thank you @brendandburns for your support !
It's true that kubernetes-client is part of a broader application ! And I am definitely not mastering all of its parts. Especially ElectronJS !
Actually, I solved this pb this weekend.

The issue was on the ElectronJS part, actually.
Clearly, the Informers were in 'stopped' status. But seems like the Electron IPC mechanism was still sending/receiving remaining events (stopped Informers still have their K8s objects list in their properties. Maybe they don't actualize K8s objects anymore, but still send messages ?).

ElectronJS trick is to remove all Listeners on the given channel when destroying the view (my views are managed by VueJS and navigation by Vue-Router).
exemple:
ipcRenderer.removeAllListeners('pods-from-namespace')

This cleans up the IPC channel created by the view. Therefore, going back & forth to this view does not accumulate events anymore.
And you're right, I will trigger a new event on the view to stop Informers when destroying the view (I keep track of them in an Array, now).

Thanks again for the work on the project 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants