statelyai / inspect

Inspect state transitions and actor communication
https://stately.ai/docs/inspector
MIT License
45 stars 5 forks source link

Possible memory leak within createBrowserInspector #29

Closed SargisPlusPlus closed 4 months ago

SargisPlusPlus commented 5 months ago

I have a single page application. There is a button on my page that starts a flow for a user. This flow implements an xstate.

Within each flow, I create createBrowserInspector.

  const { inspect } = createBrowserInspector({
    autoStart: false,
  });

After which I create an state machine actor.

  const actor = createActorContext(transcriptionMachine, {
     inspect: isDevMode ? inspect : undefined,
  });

Users can start this flow as many times as they wish within a single page. Thus, createBrowserInspector and createActorContext are called as many times as users engage with the flow. What we have noticed is that after multiple flows, the memory of the Chrome tab keeps growing, reaching to couple of gigabytes.

To fix this memory problem, I had to put createBrowserInspector behind a flag altogether:

  const getInspect = () => {
     if (isDevMode) {
          const { inspect } = createBrowserInspector({
            autoStart: false,
          });
          return inspect;
     }
     return undefined;
  }

  const actor = createActorContext(transcriptionMachine, {
     inspect: getInspect(),
  });
davidkpiano commented 5 months ago

Is it possible to make a minimal reproduction of this so I can debug it?

cpetzel commented 5 months ago

I suspect the issue is here. Every single event is stored memory.

All of the events are queued up so that a web inspector can attach at a later time. If the user refreshes the tab, then they would again receive all of the events.

cpetzel commented 4 months ago

I have tried to address this in my react native inspector... Instead of queueing up deferred events while we wait for a new connection, the inspector/adapter can retain references to all of the actors, then when a inspector connects, we can send over all of the active actor definitions and snapshots to the inspector. This way, we don't have to retain any events in memory.

This approach means that you won't see any previous events after you connect the inspector, but I think this is a tradeoff that most people would take.

sbaechler commented 4 months ago

Would it make sense if createBrowserInspector() would behave like a singleton? The inspector can handle multiple machines.