Project

General

Profile

Actions

Bug #42908

open

mgr/dashboard: some components call repeatedly the same method (up to 12 times/second)

Added by Ernesto Puerta over 4 years ago. Updated about 3 years ago.

Status:
In Progress
Priority:
Normal
Category:
General
Target version:
-
% Done:

0%

Source:
Tags:
Backport:
Regression:
No
Severity:
2 - major
Reviewed:
Affected Versions:
ceph-qa-suite:
Pull request ID:
Crash signature (v1):
Crash signature (v2):

Description

Due to the way Angular change detection works, functions called from templates result invoked 1 time per lifecycle hook (that's 12 times, while only once is required). Hence this might mean up to a 1100% computing increase for some components.

This issue can be fixed by either only using variables in templates or moving Angular components to OnPush change detection strategy (but that has its downsides).

In the meantime and to avoid adding further occurrences of this issue, tslint no-template-call-expression should be enabled (additionally, other Angular specific rules should be enabled).

The complete list of component affected by this anti-pattern are:

     42 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-form/iscsi-target-form.component.html
     38 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html
     37 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.html
     23 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.html
     16 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.html
     15 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html
     13 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration-form/configuration-form.component.html
     11 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.html
     10 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-module-form/mgr-module-form.component.html
      8 /ceph/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.html
      8 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.html
      8 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form-client/nfs-form-client.component.html
      8 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-discovery-modal/iscsi-target-discovery-modal.component.html
      7 /ceph/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-password-form/user-password-form.component.html
      7 /ceph/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.html
      6 /ceph/src/pybind/mgr/dashboard/frontend/src/app/shared/components/config-option/config-option.component.html
      6 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/pool-edit-peer-modal/pool-edit-peer-modal.component.html
      5 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-subuser-modal/rgw-user-subuser-modal.component.html
      5 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-s3-key-modal/rgw-user-s3-key-modal.component.html
      5 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.html
      5 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-form/silence-form.component.html
      4 /ceph/src/pybind/mgr/dashboard/frontend/src/app/shared/components/select/select.component.html
      4 /ceph/src/pybind/mgr/dashboard/frontend/src/app/core/auth/role-form/role-form.component.html
      4 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-matcher-modal/silence-matcher-modal.component.html
      4 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.html
      4 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/host-details/host-details.component.html
      4 /ceph/src/pybind/mgr/dashboard/frontend/src/app/app.component.html
      3 /ceph/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.html
      3 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health/health.component.html
      3 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-namespace-form/rbd-namespace-form.component.html
      3 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-configuration-form/rbd-configuration-form.component.html
      2 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-capability-modal/rgw-user-capability-modal.component.html
      2 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/host-form/host-form.component.html
      2 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/crushmap/crushmap.component.html
      2 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-move-modal/rbd-trash-move-modal.component.html
      2 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-image-settings-modal/iscsi-target-image-settings-modal.component.html
      2 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.html
      2 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-setting/iscsi-setting.component.html
      1 /ceph/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.html
      1 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-daemon-details/rgw-daemon-details.component.html
      1 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-list/silence-list.component.html
      1 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.html
      1 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-detail/cephfs-detail.component.html
      1 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-restore-modal/rbd-trash-restore-modal.component.html
      1 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-list/rbd-trash-list.component.html
      1 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-form/rbd-snapshot-form.component.html
      1 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/pool-edit-mode-modal/pool-edit-mode-modal.component.html
      1 /ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-iqn-settings-modal/iscsi-target-iqn-settings-modal.component.html


Related issues 3 (0 open3 closed)

Related to Dashboard - Cleanup #46375: mgr/dashboard: Slow pool detail tab switchesResolvedStephan Müller

Actions
Related to Dashboard - Bug #48181: mgr/dashboard: Use pipe instead of calling function within template wherever possibleResolvedVolker Theile

Actions
Has duplicate Dashboard - Cleanup #48051: mgr/dashboard: Use pipe instead of calling function within templateResolvedVolker Theile

Actions
Actions #1

Updated by Ernesto Puerta over 4 years ago

  • Status changed from New to In Progress
Actions #2

Updated by Ernesto Puerta over 3 years ago

  • Related to Cleanup #46375: mgr/dashboard: Slow pool detail tab switches added
Actions #3

Updated by Ernesto Puerta over 3 years ago

  • Has duplicate Cleanup #48051: mgr/dashboard: Use pipe instead of calling function within template added
Actions #4

Updated by Volker Theile over 3 years ago

I enabled 'template-no-call-expression' to identify all function calls in templates. It comes out that not all calls can be replaced by the 'pipeFunction' pipe because you need to have a value that Angular can identify when it has been changed. Examples that can not be fixed by using 'pipeFunction':

            <span class="invalid-feedback" 
                  *ngIf="userForm.showError('uid', frm, 'required')" 
                  i18n>This field is required.</span>
    <button class="btn btn-{{btnColor}} dropdown-toggle-split" 
            *ngIf="showDropDownActions()" 
            ngbDropdownToggle>
  <ngx-datatable #table
                 ...
                 [rowClass]="getRowClass()" 
                 ...
                 [rowIdentity]="rowIdentity()" 
                 [rowHeight]="'auto'">

So we can not enable 'template-no-call-expression' in general, but use it to identify the code that can be modified by using 'pipeFunction'.

Actions #5

Updated by Ernesto Puerta over 3 years ago

Yeah, memoization only works with pure functions.

*ngIf="userForm.showError('uid', frm, 'required')"

The only way for that function to work is that the output of showError only depends on ('uid', frm, 'required'). If that were the case, that function should be rewritten to support a single array argument:

{{ ['uid', frm, 'required'] | pipeFunction : showError }}

For functions that don't have any input argument (like *ngIf="showDropDownActions()"), the only way would be to pass the context by means of call:

*ngIf="this | pipeFunction: showDropDownActions.call" 

However that will probably not work too well... So after having a look at the code, for that specific case, I think the issue is with the function itself and its design: that ngIf should check a simple condition, and the change detection logic (updateDropDownActions) should be driven from the event causing that change, not from the template...

*ngIf="dropDownActions.length > 1" 

And on the last example, I think that is completely different, because here we just want to pass a "function object" as any other kind of static object, not to invoke that object to create another function object on every change detection (am I right?). So by removing a calling layer should work ok:


  getRowClass() {                                                                                                                                                                                                                                                                           
    return {                                                                                       
      clickable: !_.isUndefined(this.selectionType)                                                
    };                                                                                             
  }  
...
  <ngx-datatable #table
                 ...
                 [rowClass]="getRowClass"  // vs. getRowClass()
                 ...
Actions #6

Updated by Ernesto Puerta over 3 years ago

  • Related to Bug #48181: mgr/dashboard: Use pipe instead of calling function within template wherever possible added
Actions #7

Updated by Ernesto Puerta about 3 years ago

  • Project changed from mgr to Dashboard
  • Category changed from 132 to General
Actions

Also available in: Atom PDF