You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by bk...@apache.org on 2022/09/13 15:52:21 UTC

[superset] branch master updated: perf: Memoize the common_bootstrap_payload and include user param (#21018) (#21439)

This is an automated email from the ASF dual-hosted git repository.

bkyryliuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 44654e5abc perf: Memoize the common_bootstrap_payload and include user param (#21018) (#21439)
44654e5abc is described below

commit 44654e5abcc70d1fd934529dc82d7fe1d1bb20ef
Author: Bogdan <b....@gmail.com>
AuthorDate: Tue Sep 13 08:52:08 2022 -0700

    perf: Memoize the common_bootstrap_payload and include user param (#21018) (#21439)
    
    Co-authored-by: Bogdan Kyryliuk <bo...@dropbox.com>
---
 superset/embedded/view.py             |  4 ++--
 superset/views/base.py                | 27 +++++++++++++++++----------
 superset/views/core.py                | 10 +++++-----
 superset/views/dashboard/views.py     |  2 +-
 tests/integration_tests/core_tests.py |  4 +++-
 5 files changed, 28 insertions(+), 19 deletions(-)

diff --git a/superset/embedded/view.py b/superset/embedded/view.py
index b64bcf6fc2..c3c6c39ba2 100644
--- a/superset/embedded/view.py
+++ b/superset/embedded/view.py
@@ -17,7 +17,7 @@
 import json
 from typing import Callable
 
-from flask import abort, request
+from flask import abort, g, request
 from flask_appbuilder import expose
 from flask_login import AnonymousUserMixin, LoginManager
 from flask_wtf.csrf import same_origin
@@ -77,7 +77,7 @@ class EmbeddedView(BaseSupersetView):
         )
 
         bootstrap_data = {
-            "common": common_bootstrap_payload(),
+            "common": common_bootstrap_payload(g.user),
             "embedded": {
                 "dashboard_id": embedded.dashboard_id,
             },
diff --git a/superset/views/base.py b/superset/views/base.py
index 330ca0b04e..b790ca709c 100644
--- a/superset/views/base.py
+++ b/superset/views/base.py
@@ -39,6 +39,7 @@ from flask_appbuilder import BaseView, Model, ModelView
 from flask_appbuilder.actions import action
 from flask_appbuilder.forms import DynamicForm
 from flask_appbuilder.models.sqla.filters import BaseFilter
+from flask_appbuilder.security.sqla.models import User
 from flask_appbuilder.widgets import ListWidget
 from flask_babel import get_locale, gettext as __, lazy_gettext as _
 from flask_jwt_extended.exceptions import NoAuthorizationError
@@ -71,6 +72,7 @@ from superset.exceptions import (
     SupersetException,
     SupersetSecurityException,
 )
+from superset.extensions import cache_manager
 from superset.models.helpers import ImportExportMixin
 from superset.reports.models import ReportRecipientType
 from superset.superset_typing import FlaskResponse
@@ -284,7 +286,7 @@ class BaseSupersetView(BaseView):
     def render_app_template(self) -> FlaskResponse:
         payload = {
             "user": bootstrap_user_data(g.user, include_perms=True),
-            "common": common_bootstrap_payload(),
+            "common": common_bootstrap_payload(g.user),
         }
         return self.render_template(
             "superset/spa.html",
@@ -295,7 +297,7 @@ class BaseSupersetView(BaseView):
         )
 
 
-def menu_data() -> Dict[str, Any]:
+def menu_data(user: User) -> Dict[str, Any]:
     menu = appbuilder.menu.get_data()
 
     languages = {}
@@ -340,22 +342,27 @@ def menu_data() -> Dict[str, Any]:
             "build_number": build_number,
             "languages": languages,
             "show_language_picker": len(languages.keys()) > 1,
-            "user_is_anonymous": g.user.is_anonymous,
+            "user_is_anonymous": user.is_anonymous,
             "user_info_url": None
             if appbuilder.app.config["MENU_HIDE_USER_INFO"]
             else appbuilder.get_url_for_userinfo,
             "user_logout_url": appbuilder.get_url_for_logout,
             "user_login_url": appbuilder.get_url_for_login,
             "user_profile_url": None
-            if g.user.is_anonymous or appbuilder.app.config["MENU_HIDE_USER_INFO"]
-            else f"/superset/profile/{g.user.username}",
+            if user.is_anonymous or appbuilder.app.config["MENU_HIDE_USER_INFO"]
+            else f"/superset/profile/{user.username}",
             "locale": session.get("locale", "en"),
         },
     }
 
 
-def common_bootstrap_payload() -> Dict[str, Any]:
-    """Common data always sent to the client"""
+@cache_manager.cache.memoize(timeout=60)
+def common_bootstrap_payload(user: User) -> Dict[str, Any]:
+    """Common data always sent to the client
+
+    The function is memoized as the return value only changes based
+    on configuration and feature flag values.
+    """
     messages = get_flashed_messages(with_categories=True)
     locale = str(get_locale())
 
@@ -388,7 +395,7 @@ def common_bootstrap_payload() -> Dict[str, Any]:
         "extra_sequential_color_schemes": conf["EXTRA_SEQUENTIAL_COLOR_SCHEMES"],
         "extra_categorical_color_schemes": conf["EXTRA_CATEGORICAL_COLOR_SCHEMES"],
         "theme_overrides": conf["THEME_OVERRIDES"],
-        "menu_data": menu_data(),
+        "menu_data": menu_data(user),
     }
     bootstrap_data.update(conf["COMMON_BOOTSTRAP_OVERRIDES_FUNC"](bootstrap_data))
     return bootstrap_data
@@ -499,7 +506,7 @@ def show_unexpected_exception(ex: Exception) -> FlaskResponse:
 def get_common_bootstrap_data() -> Dict[str, Any]:
     def serialize_bootstrap_data() -> str:
         return json.dumps(
-            {"common": common_bootstrap_payload()},
+            {"common": common_bootstrap_payload(g.user)},
             default=utils.pessimistic_json_iso_dttm_ser,
         )
 
@@ -517,7 +524,7 @@ class SupersetModelView(ModelView):
     def render_app_template(self) -> FlaskResponse:
         payload = {
             "user": bootstrap_user_data(g.user, include_perms=True),
-            "common": common_bootstrap_payload(),
+            "common": common_bootstrap_payload(g.user),
         }
         return self.render_template(
             "superset/spa.html",
diff --git a/superset/views/core.py b/superset/views/core.py
index aba559c2ed..859b42ad97 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -928,7 +928,7 @@ class Superset(BaseSupersetView):  # pylint: disable=too-many-public-methods
             "force": force,
             "user": bootstrap_user_data(g.user, include_perms=True),
             "forced_height": request.args.get("height"),
-            "common": common_bootstrap_payload(),
+            "common": common_bootstrap_payload(g.user),
         }
         if slc:
             title = slc.slice_name
@@ -1932,7 +1932,7 @@ class Superset(BaseSupersetView):  # pylint: disable=too-many-public-methods
 
         bootstrap_data = {
             "user": bootstrap_user_data(g.user, include_perms=True),
-            "common": common_bootstrap_payload(),
+            "common": common_bootstrap_payload(g.user),
         }
 
         return self.render_template(
@@ -2673,7 +2673,7 @@ class Superset(BaseSupersetView):  # pylint: disable=too-many-public-methods
 
         payload = {
             "user": bootstrap_user_data(g.user, include_perms=True),
-            "common": common_bootstrap_payload(),
+            "common": common_bootstrap_payload(g.user),
         }
 
         return self.render_template(
@@ -2702,7 +2702,7 @@ class Superset(BaseSupersetView):  # pylint: disable=too-many-public-methods
 
         payload = {
             "user": bootstrap_user_data(user, include_perms=True),
-            "common": common_bootstrap_payload(),
+            "common": common_bootstrap_payload(g.user),
         }
 
         return self.render_template(
@@ -2766,7 +2766,7 @@ class Superset(BaseSupersetView):  # pylint: disable=too-many-public-methods
         """SQL Editor"""
         payload = {
             "defaultDbId": config["SQLLAB_DEFAULT_DBID"],
-            "common": common_bootstrap_payload(),
+            "common": common_bootstrap_payload(g.user),
             **self._get_sqllab_tabs(get_user_id()),
         }
 
diff --git a/superset/views/dashboard/views.py b/superset/views/dashboard/views.py
index 8d562fefbc..32f0189d70 100644
--- a/superset/views/dashboard/views.py
+++ b/superset/views/dashboard/views.py
@@ -158,7 +158,7 @@ class Dashboard(BaseSupersetView):
         )
 
         bootstrap_data = {
-            "common": common_bootstrap_payload(),
+            "common": common_bootstrap_payload(g.user),
             "embedded": {"dashboard_id": dashboard_id_or_slug},
         }
 
diff --git a/tests/integration_tests/core_tests.py b/tests/integration_tests/core_tests.py
index 5f31224624..75314c1533 100644
--- a/tests/integration_tests/core_tests.py
+++ b/tests/integration_tests/core_tests.py
@@ -62,7 +62,7 @@ from superset.connectors.sqla.models import SqlaTable
 from superset.db_engine_specs.base import BaseEngineSpec
 from superset.db_engine_specs.mssql import MssqlEngineSpec
 from superset.exceptions import SupersetException
-from superset.extensions import async_query_manager
+from superset.extensions import async_query_manager, cache_manager
 from superset.models import core as models
 from superset.models.annotations import Annotation, AnnotationLayer
 from superset.models.dashboard import Dashboard
@@ -1434,6 +1434,8 @@ class TestCore(SupersetTestCase):
         """
         Functions in feature flags don't break bootstrap data serialization.
         """
+        # feature flags are cached
+        cache_manager.cache.clear()
         self.login()
 
         encoded = json.dumps(