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

Performance Issues with Dev Tools + Router-Store #97

Closed
eppsilon opened this issue Jul 18, 2017 · 12 comments
Closed

Performance Issues with Dev Tools + Router-Store #97

eppsilon opened this issue Jul 18, 2017 · 12 comments

Comments

@eppsilon
Copy link

I've found that my app becomes quite slow if StoreRouterConnectingModule and StoreDevtoolsModule are both imported.

StoreModule.forRoot(reducers),
...(!environment.production ? [StoreDevtoolsModule.instrument({ maxAge: 10 })] : []),
EffectsModule.forRoot([]),
StoreRouterConnectingModule,

Observations

  • CPU usage by Chrome is at least 30% constantly. In the Chrome task manager I've seen the dev tools extension process use 100%+ CPU and 2+ GB of RAM.
  • I started with maxAge: 50, changing it to as low as maxAge: 2 didn't help.
  • Performance seemed to be worse if I flipped the order of EffectsModule and StoreRouterConnectingModule.
  • I found that exporting the state from the dev tools yielded a 27 MB JSON file. If the StoreRouterConnectingModule was removed, it was 71 KB.
  • Most of the 27 MB file has data like this:
 "computedStates": [
    {
      "state": {
        "router": {
          "state": {
            "_root": {
              "value": {
                "url": [],
                "params": {},
                "queryParams": {},
                "data": {},
                "outlet": "primary",
                "_routeConfig": null,
                "_urlSegment": {
                  "segments": [],
                  "children": {
                    "primary": {
                      "segments": [
                        {
                          "path": "app",
                          "parameters": {}
                        },
                        {
                          "path": "users",
                          "parameters": {}
                        }
                      ],
                      "children": {},
                      "parent": {
                        "segments": {
                          "$jsan": "$.computedStates[0].state.router.state._root.value._urlSegment.segments"
                        },
                        "children": {
                          "$jsan": "$.computedStates[0].state.router.state._root.value._urlSegment.children"
                        },
                        "parent": null,
                        "_sourceSegment": {
                          "$jsan": "$.computedStates[0].state.router.state._root.value._urlSegment"
                        },
                        "_segmentIndexShift": 0
                      }
                    }
                  },
                  "parent": null
                },
                "_lastPathIndex": -1,
                "_resolve": {},
                "_routerState": {
                  "$jsan": "$.computedStates[0].state.router.state"
                }
              },

Profiling Results

Top of call tree:

screen shot 2017-07-18 at 13 16 38

Next highest in tree:

screen shot 2017-07-18 at 13 19 17

Versions

Angular: 4.2.6
Angular CLI: 1.2.1
NGRX: 4.0.0

Browser: Chrome 59.0.3071.115
OS: macOS 10.12.5
Node: 6.11.0
Yarn: 0.27.5

@shlomiassaf
Copy link

This is because the router state stores the RouterStateSnapshot object.

That object is huge, holding components, router tress and more goodies.

Redux dev tools tries to render it (json) deep which is a huge task.

It probably holds circular references which I don't know if the dev tools handles or not, but anyway its a heavy task.

@nihique
Copy link

nihique commented Jul 19, 2017

+1, I had to disable Redux DevTools extension to be able to work on app...

@Kaffiend
Copy link

+1 Likewise, unusable with router-store. Once i removed the router from state, its fine.

@crysislinux
Copy link

so I just the ngrx-store-logger. there is a big performance problem to integrate with Redux DevTools

@alexander-heimbuch
Copy link

Same here, on first init with redux dev tools opened the app crashes. Afterwards it is noticeable slower with devtools activated.

@bfricka
Copy link
Contributor

bfricka commented Jul 21, 2017

I've had similar probably with large objects in Redux Devtools previously. A way we can get around it is to wrap router states so they implement toJSON, and then manually trim down that object.

As long as we implement this on the router store object specifically (without messing with the snapshots), this shouldn't have any downstream consequences.

E.g. in router_store_module.ts, change:

return {
  state: action.payload.routerState,
  navigationId: action.payload.event.id,
};

to

return new StoreRouterState(
  action.payload.routerState,
  action.payload.event.id,
);

and then implementing StoreRouterState something like:

class RouterState {
  constructor(public state: RouterStateSnapshot, public id: number) {}
  toJSON() {
     // Truncate router object however
     return {state: {url: 'foo'}, id: this.id};
  }
}

Whether this is actually a good idea is another matter entirely. A better solution might be to allow a deserializer map to be passed to store devtools. @brandonroberts any insight here? Can this be accomplished with StoreDevtoolsConfig.monitor?

@endyjasmi
Copy link

Any news on @bfricka suggestions? I think that might work.

@brandonroberts
Copy link
Member

brandonroberts commented Jul 24, 2017

@bfricka I talked with @vsavkin about this issue and we're going to implement a serializer for the RouterState that you can override. The RouterState is a large complex structure, but generally you may only need a small piece of it. By default, you'll get the entire RouterState, but there will be an interface you can implement to pair it down to only what you need.

This will make it easier to work with the Devtools, as if you only need the URL, the payload will be significantly smaller.

@cport1
Copy link

cport1 commented Aug 2, 2017

Do we have an ETA for this issue?

@timblakely
Copy link
Contributor

This also makes it impossible to serialize the state via JSON.stringify since there are circular references: ERROR TypeError: Converting circular structure to JSON

brandonroberts added a commit that referenced this issue Aug 6, 2017
This adds a serializer that can be customized for returning the router state. By default, the entire RouterStateSnapshot is returned. A custom serializer can be provided to parse the snapshot into a more managable structure.

Closes #104, #97
brandonroberts added a commit that referenced this issue Aug 6, 2017
This adds a serializer that can be customized for returning the router state. By default, the entire RouterStateSnapshot is returned. A custom serializer can be provided to parse the snapshot into a more managable structure.

Closes #104, #97
MikeRyanDev pushed a commit that referenced this issue Aug 9, 2017
This adds a serializer that can be customized for returning the router state snapshot. By default, the entire RouterStateSnapshot is returned. Documentation has been updated with example usage.

```ts
import { StoreModule } from '@ngrx/store';
import {
  StoreRouterConnectingModule,
  routerReducer,
  RouterStateSerializer
} from '@ngrx/router-store';

export interface RouterStateUrl {
  url: string;
}

export class CustomSerializer implements RouterStateSerializer<RouterStateUrl> {
  serialize(routerState: RouterStateSnapshot): RouterStateUrl {
    const url = routerState ? routerState.url : '';

    // Only return an object including the URL
    // instead of the entire snapshot
    return { url };
  }
}

@NgModule({
  imports: [
    StoreModule.forRoot({ routerReducer: routerReducer }),
    RouterModule.forRoot([
      // routes
    ]),
    StoreRouterConnectingModule
  ],
  providers: [
    { provide: RouterStateSerializer, useClass: CustomSerializer }
  ]
})
export class AppModule { }
```

Closes #97, #104, #237
@CharlieGreenman
Copy link

CharlieGreenman commented Jan 9, 2018

Anyone looking for an ETA on this issue, feel free to take a look at the following documentation. It offers a solution using a Custom Router State Serializer.

Update 9/21/2019: for RxJs and up can go to (https://ngrx.io/guide/router-store/configuration#custom-router-state-serializer)

@antl3x
Copy link

antl3x commented Mar 27, 2018

News guys ?

oboukli added a commit to oboukli/ngrx-platform that referenced this issue Jun 9, 2018
Update note on issue ngrx#97 in MIGRATION.md
Update examples to RxJS 6
Edit and clean up
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests