zalmoxisus / redux-devtools-extension

Redux DevTools extension.
MIT License
13.49k stars 1.01k forks source link

Optimize `ngrx/store-devtools` #314

Open btopro opened 7 years ago

btopro commented 7 years ago

@heymp found an interesting issue the other day. We've built an Angular2 based collaborative one-page app that lets people post images and then comment on them. Nothing crazy. Well, we started to notice if we throw like 100+ objects to present on the UI that the first load is fine, but any change to any object after that causes the app to run horrifically slow to the point of being unusable.

I was told on twitter to post here so not complaining just didn't realize this was what was happening and poor @heymp refactored our whole app to no avail till he realized it was the plugin.

zalmoxisus commented 7 years ago

Thanks for the report. Do you use the extension with Redux as usually or with integrations like ngrx/store, ng2-redux...?

zalmoxisus commented 7 years ago

Any details on this? I don't see any leaks on the extension part.

I assume you're using ngrx/store-devtools, which is sending the whole lifted data on every update even when it shouldn't?

heyMP commented 7 years ago

Yeah, it's using ngrx/store-devtools. And we are using redux for pretty heavily: routeing, crud http operations, etc. When testing with minimal content, the app works fine, redux devtools time travelling works fine. But when we generate a bulk of content we roughly benchmarked the following:

list of 100 text-based submissions -> navigate to a handful of submissions -> create json export from redux devtools extension
This will result in a file size around 50mb.

Does that sound outlandish of a file size?

zalmoxisus commented 7 years ago

I'm not familiar with ngrx/store-devtools, but @born2net said he solved a similar case by using it as following:

StoreDevtoolsModule.instrumentStore({maxAge: 2}),

However, that can be addressed better on ngrx/store-devtools part to:

  1. not send data when the extension is not opened (react on START / STOP messages received from the extension, and additionally supporting PAUSE_RECORDING action would be useful too).
  2. send the whole lifted state only on START, except that send only the current state and action (send(action, state)).
  3. have a default maxAge value (I'd suggest 50 like we use for Redux) to not allow growing the liftedState infinitely.

Other than that, there's nothing we can do from our part.

\cc @MikeRyan52

born2net commented 7 years ago

yes in our very large redux store, changing to StoreDevtoolsModule.instrumentStore({maxAge: 2}), solved it

heyMP commented 7 years ago

That definitely helps. Thanks for the help!

guojenman commented 7 years ago

maxAge = 2 makes no difference for me. All I can do for now is to remove it from my production build. I have a very large store and multi-user support, with the server replaying back all actions to all connected clients. With the StoreDevtools, actions start to take 2-4x as long after around 400 actions.

Left it on for my dev build since it's so indispensable :)

Braincompiler commented 7 years ago

I found out that tryCatchStringify seems to be the bottleneck. bildschirmfoto 2017-06-06 um 12 46 47

It's part of the redux-devtools' API which is used by @ngrx/store-devtools. With @angular-redux/store there is no problem with devtools cause it's not using the API. There you are integrate the devtools directly.

zalmoxisus commented 7 years ago

@Braincompiler of course JSON.stringify, which it invokes, is the bottleneck. What I was suggested above would help not to have so large amount of data to stringify and not invoke the stringify when not necessary, but as written it can be done only from @ngrx/store-devtools part as it sends/receives data to/from the extension. The idea is that a browser extension is in a separate process and you have to stringify the data in order the extension to receive it.

Braincompiler commented 7 years ago

@zalmoxisus I get the idea, I just wanted to provide the information about what I found out :)

mightyaleksey commented 6 years ago

Hi, looks like a faced a similar problem with tryCatchStringify function. In my case we have an array of ~40000 elements in the redux store and JSON.stringify makes store updates slower.

Action and state sanitizers can help a lot here. I made a replacement for the big values before serialization and it resulted in significant performance boost.

javico2609 commented 5 years ago

HI, In my case we have Observable.Websocket that emits several msg per second and the tools freeze complete my browser.

javico2609 commented 5 years ago

there are the way to disabled the DevTools on fly ?

gionkunz commented 5 years ago

Maybe FP structural sharing for storing state history would help solving the memory issues that come with the devtools with large datasets, just throwing in two cents.