prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Cache filters API response for web UI
  As the number of actions, rules, etc has grown in our StackStorm cluster, the UI is becoming slower. In particular, the filters view (which we rely on heavily with this many executions) is very slow to become visible and usable. It might help to cache this `/api/v1/executions/views/filters` API response and asynchronously reload this value outside of the request thread, since this is slowly-changing data.
  
  Our typical workflow is to
  1. load the main execution page
  2. wait for the filters to appear
  3. apply some set of filters
  4. wait for the next page to load
  
  Here's a waterfall showing a 20s load time for the filters response. This is pretty common for us now.
  ![screen shot 2016-08-22 at 12 20 14 pm](https://cloud.githubusercontent.com/assets/44062/17864407/8c624f68-6863-11e6-8557-c5fbe4d23e9f.png)
  
  For reference, we have 572 rules, 1200 actions, 143 triggers, 19 trigger types, and 600k+ executions.
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `st2common/st2common/models/db/execution.py`
  Content:
  ```
  1 # Licensed to the StackStorm, Inc ('StackStorm') under one or more
  2 # contributor license agreements.  See the NOTICE file distributed with
  3 # this work for additional information regarding copyright ownership.
  4 # The ASF licenses this file to You under the Apache License, Version 2.0
  5 # (the "License"); you may not use this file except in compliance with
  6 # the License.  You may obtain a copy of the License at
  7 #
  8 #     http://www.apache.org/licenses/LICENSE-2.0
  9 #
  10 # Unless required by applicable law or agreed to in writing, software
  11 # distributed under the License is distributed on an "AS IS" BASIS,
  12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 # See the License for the specific language governing permissions and
  14 # limitations under the License.
  15 
  16 import copy
  17 
  18 import mongoengine as me
  19 
  20 from st2common import log as logging
  21 from st2common.models.db import stormbase
  22 from st2common.fields import ComplexDateTimeField
  23 from st2common.util import date as date_utils
  24 from st2common.util.secrets import get_secret_parameters
  25 from st2common.util.secrets import mask_secret_parameters
  26 from st2common.constants.types import ResourceType
  27 
  28 __all__ = [
  29     'ActionExecutionDB'
  30 ]
  31 
  32 
  33 LOG = logging.getLogger(__name__)
  34 
  35 
  36 class ActionExecutionDB(stormbase.StormFoundationDB):
  37     RESOURCE_TYPE = ResourceType.EXECUTION
  38     UID_FIELDS = ['id']
  39 
  40     trigger = stormbase.EscapedDictField()
  41     trigger_type = stormbase.EscapedDictField()
  42     trigger_instance = stormbase.EscapedDictField()
  43     rule = stormbase.EscapedDictField()
  44     action = stormbase.EscapedDictField(required=True)
  45     runner = stormbase.EscapedDictField(required=True)
  46     # Only the diff between the liveaction type and what is replicated
  47     # in the ActionExecutionDB object.
  48     liveaction = stormbase.EscapedDictField(required=True)
  49     status = me.StringField(
  50         required=True,
  51         help_text='The current status of the liveaction.')
  52     start_timestamp = ComplexDateTimeField(
  53         default=date_utils.get_datetime_utc_now,
  54         help_text='The timestamp when the liveaction was created.')
  55     end_timestamp = ComplexDateTimeField(
  56         help_text='The timestamp when the liveaction has finished.')
  57     parameters = stormbase.EscapedDynamicField(
  58         default={},
  59         help_text='The key-value pairs passed as to the action runner & action.')
  60     result = stormbase.EscapedDynamicField(
  61         default={},
  62         help_text='Action defined result.')
  63     context = me.DictField(
  64         default={},
  65         help_text='Contextual information on the action execution.')
  66     parent = me.StringField()
  67     children = me.ListField(field=me.StringField())
  68     log = me.ListField(field=me.DictField())
  69     # Do not use URLField for web_url. If host doesn't have FQDN set, URLField validation blows.
  70     web_url = me.StringField(required=False)
  71 
  72     meta = {
  73         'indexes': [
  74             {'fields': ['rule.ref']},
  75             {'fields': ['action.ref']},
  76             {'fields': ['liveaction.id']},
  77             {'fields': ['start_timestamp']},
  78             {'fields': ['end_timestamp']},
  79             {'fields': ['status']},
  80             {'fields': ['parent']},
  81             {'fields': ['-start_timestamp', 'action.ref', 'status']}
  82         ]
  83     }
  84 
  85     def get_uid(self):
  86         # TODO Construct od from non id field:
  87         uid = [self.RESOURCE_TYPE, str(self.id)]
  88         return ':'.join(uid)
  89 
  90     def mask_secrets(self, value):
  91         result = copy.deepcopy(value)
  92 
  93         execution_parameters = value['parameters']
  94         parameters = {}
  95         # pylint: disable=no-member
  96         parameters.update(value.get('action', {}).get('parameters', {}))
  97         parameters.update(value.get('runner', {}).get('runner_parameters', {}))
  98 
  99         secret_parameters = get_secret_parameters(parameters=parameters)
  100         result['parameters'] = mask_secret_parameters(parameters=execution_parameters,
  101                                                       secret_parameters=secret_parameters)
  102         return result
  103 
  104     def get_masked_parameters(self):
  105         """
  106         Retrieve parameters with the secrets masked.
  107 
  108         :rtype: ``dict``
  109         """
  110         serializable_dict = self.to_serializable_dict(mask_secrets=True)
  111         return serializable_dict['parameters']
  112 
  113 
  114 MODELS = [ActionExecutionDB]
  115
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/st2common/st2common/models/db/execution.py b/st2common/st2common/models/db/execution.py
  --- a/st2common/st2common/models/db/execution.py
  +++ b/st2common/st2common/models/db/execution.py
  @@ -78,6 +78,11 @@
               {'fields': ['end_timestamp']},
               {'fields': ['status']},
               {'fields': ['parent']},
  +            {'fields': ['rule.name']},
  +            {'fields': ['runner.name']},
  +            {'fields': ['trigger.name']},
  +            {'fields': ['trigger_type.name']},
  +            {'fields': ['context.user']},
               {'fields': ['-start_timestamp', 'action.ref', 'status']}
           ]
       }
  
verification_info: {"golden_diff": "diff --git a/st2common/st2common/models/db/execution.py b/st2common/st2common/models/db/execution.py\n--- a/st2common/st2common/models/db/execution.py\n+++ b/st2common/st2common/models/db/execution.py\n@@ -78,6 +78,11 @@\n             {'fields': ['end_timestamp']},\n             {'fields': ['status']},\n             {'fields': ['parent']},\n+            {'fields': ['rule.name']},\n+            {'fields': ['runner.name']},\n+            {'fields': ['trigger.name']},\n+            {'fields': ['trigger_type.name']},\n+            {'fields': ['context.user']},\n             {'fields': ['-start_timestamp', 'action.ref', 'status']}\n         ]\n     }\n", "issue": "Cache filters API response for web UI\nAs the number of actions, rules, etc has grown in our StackStorm cluster, the UI is becoming slower. In particular, the filters view (which we rely on heavily with this many executions) is very slow to become visible and usable. It might help to cache this `/api/v1/executions/views/filters` API response and asynchronously reload this value outside of the request thread, since this is slowly-changing data.\n\nOur typical workflow is to\n1. load the main execution page\n2. wait for the filters to appear\n3. apply some set of filters\n4. wait for the next page to load\n\nHere's a waterfall showing a 20s load time for the filters response. This is pretty common for us now.\n![screen shot 2016-08-22 at 12 20 14 pm](https://cloud.githubusercontent.com/assets/44062/17864407/8c624f68-6863-11e6-8557-c5fbe4d23e9f.png)\n\nFor reference, we have 572 rules, 1200 actions, 143 triggers, 19 trigger types, and 600k+ executions.\n\n", "before_files": [{"content": "# Licensed to the StackStorm, Inc ('StackStorm') under one or more\n# contributor license agreements.  See the NOTICE file distributed with\n# this work for additional information regarding copyright ownership.\n# The ASF licenses this file to You under the Apache License, Version 2.0\n# (the \"License\"); you may not use this file except in compliance with\n# the License.  You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport copy\n\nimport mongoengine as me\n\nfrom st2common import log as logging\nfrom st2common.models.db import stormbase\nfrom st2common.fields import ComplexDateTimeField\nfrom st2common.util import date as date_utils\nfrom st2common.util.secrets import get_secret_parameters\nfrom st2common.util.secrets import mask_secret_parameters\nfrom st2common.constants.types import ResourceType\n\n__all__ = [\n    'ActionExecutionDB'\n]\n\n\nLOG = logging.getLogger(__name__)\n\n\nclass ActionExecutionDB(stormbase.StormFoundationDB):\n    RESOURCE_TYPE = ResourceType.EXECUTION\n    UID_FIELDS = ['id']\n\n    trigger = stormbase.EscapedDictField()\n    trigger_type = stormbase.EscapedDictField()\n    trigger_instance = stormbase.EscapedDictField()\n    rule = stormbase.EscapedDictField()\n    action = stormbase.EscapedDictField(required=True)\n    runner = stormbase.EscapedDictField(required=True)\n    # Only the diff between the liveaction type and what is replicated\n    # in the ActionExecutionDB object.\n    liveaction = stormbase.EscapedDictField(required=True)\n    status = me.StringField(\n        required=True,\n        help_text='The current status of the liveaction.')\n    start_timestamp = ComplexDateTimeField(\n        default=date_utils.get_datetime_utc_now,\n        help_text='The timestamp when the liveaction was created.')\n    end_timestamp = ComplexDateTimeField(\n        help_text='The timestamp when the liveaction has finished.')\n    parameters = stormbase.EscapedDynamicField(\n        default={},\n        help_text='The key-value pairs passed as to the action runner & action.')\n    result = stormbase.EscapedDynamicField(\n        default={},\n        help_text='Action defined result.')\n    context = me.DictField(\n        default={},\n        help_text='Contextual information on the action execution.')\n    parent = me.StringField()\n    children = me.ListField(field=me.StringField())\n    log = me.ListField(field=me.DictField())\n    # Do not use URLField for web_url. If host doesn't have FQDN set, URLField validation blows.\n    web_url = me.StringField(required=False)\n\n    meta = {\n        'indexes': [\n            {'fields': ['rule.ref']},\n            {'fields': ['action.ref']},\n            {'fields': ['liveaction.id']},\n            {'fields': ['start_timestamp']},\n            {'fields': ['end_timestamp']},\n            {'fields': ['status']},\n            {'fields': ['parent']},\n            {'fields': ['-start_timestamp', 'action.ref', 'status']}\n        ]\n    }\n\n    def get_uid(self):\n        # TODO Construct od from non id field:\n        uid = [self.RESOURCE_TYPE, str(self.id)]\n        return ':'.join(uid)\n\n    def mask_secrets(self, value):\n        result = copy.deepcopy(value)\n\n        execution_parameters = value['parameters']\n        parameters = {}\n        # pylint: disable=no-member\n        parameters.update(value.get('action', {}).get('parameters', {}))\n        parameters.update(value.get('runner', {}).get('runner_parameters', {}))\n\n        secret_parameters = get_secret_parameters(parameters=parameters)\n        result['parameters'] = mask_secret_parameters(parameters=execution_parameters,\n                                                      secret_parameters=secret_parameters)\n        return result\n\n    def get_masked_parameters(self):\n        \"\"\"\n        Retrieve parameters with the secrets masked.\n\n        :rtype: ``dict``\n        \"\"\"\n        serializable_dict = self.to_serializable_dict(mask_secrets=True)\n        return serializable_dict['parameters']\n\n\nMODELS = [ActionExecutionDB]\n", "path": "st2common/st2common/models/db/execution.py"}], "after_files": [{"content": "# Licensed to the StackStorm, Inc ('StackStorm') under one or more\n# contributor license agreements.  See the NOTICE file distributed with\n# this work for additional information regarding copyright ownership.\n# The ASF licenses this file to You under the Apache License, Version 2.0\n# (the \"License\"); you may not use this file except in compliance with\n# the License.  You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport copy\n\nimport mongoengine as me\n\nfrom st2common import log as logging\nfrom st2common.models.db import stormbase\nfrom st2common.fields import ComplexDateTimeField\nfrom st2common.util import date as date_utils\nfrom st2common.util.secrets import get_secret_parameters\nfrom st2common.util.secrets import mask_secret_parameters\nfrom st2common.constants.types import ResourceType\n\n__all__ = [\n    'ActionExecutionDB'\n]\n\n\nLOG = logging.getLogger(__name__)\n\n\nclass ActionExecutionDB(stormbase.StormFoundationDB):\n    RESOURCE_TYPE = ResourceType.EXECUTION\n    UID_FIELDS = ['id']\n\n    trigger = stormbase.EscapedDictField()\n    trigger_type = stormbase.EscapedDictField()\n    trigger_instance = stormbase.EscapedDictField()\n    rule = stormbase.EscapedDictField()\n    action = stormbase.EscapedDictField(required=True)\n    runner = stormbase.EscapedDictField(required=True)\n    # Only the diff between the liveaction type and what is replicated\n    # in the ActionExecutionDB object.\n    liveaction = stormbase.EscapedDictField(required=True)\n    status = me.StringField(\n        required=True,\n        help_text='The current status of the liveaction.')\n    start_timestamp = ComplexDateTimeField(\n        default=date_utils.get_datetime_utc_now,\n        help_text='The timestamp when the liveaction was created.')\n    end_timestamp = ComplexDateTimeField(\n        help_text='The timestamp when the liveaction has finished.')\n    parameters = stormbase.EscapedDynamicField(\n        default={},\n        help_text='The key-value pairs passed as to the action runner & action.')\n    result = stormbase.EscapedDynamicField(\n        default={},\n        help_text='Action defined result.')\n    context = me.DictField(\n        default={},\n        help_text='Contextual information on the action execution.')\n    parent = me.StringField()\n    children = me.ListField(field=me.StringField())\n    log = me.ListField(field=me.DictField())\n    # Do not use URLField for web_url. If host doesn't have FQDN set, URLField validation blows.\n    web_url = me.StringField(required=False)\n\n    meta = {\n        'indexes': [\n            {'fields': ['rule.ref']},\n            {'fields': ['action.ref']},\n            {'fields': ['liveaction.id']},\n            {'fields': ['start_timestamp']},\n            {'fields': ['end_timestamp']},\n            {'fields': ['status']},\n            {'fields': ['parent']},\n            {'fields': ['rule.name']},\n            {'fields': ['runner.name']},\n            {'fields': ['trigger.name']},\n            {'fields': ['trigger_type.name']},\n            {'fields': ['context.user']},\n            {'fields': ['-start_timestamp', 'action.ref', 'status']}\n        ]\n    }\n\n    def get_uid(self):\n        # TODO Construct od from non id field:\n        uid = [self.RESOURCE_TYPE, str(self.id)]\n        return ':'.join(uid)\n\n    def mask_secrets(self, value):\n        result = copy.deepcopy(value)\n\n        execution_parameters = value['parameters']\n        parameters = {}\n        # pylint: disable=no-member\n        parameters.update(value.get('action', {}).get('parameters', {}))\n        parameters.update(value.get('runner', {}).get('runner_parameters', {}))\n\n        secret_parameters = get_secret_parameters(parameters=parameters)\n        result['parameters'] = mask_secret_parameters(parameters=execution_parameters,\n                                                      secret_parameters=secret_parameters)\n        return result\n\n    def get_masked_parameters(self):\n        \"\"\"\n        Retrieve parameters with the secrets masked.\n\n        :rtype: ``dict``\n        \"\"\"\n        serializable_dict = self.to_serializable_dict(mask_secrets=True)\n        return serializable_dict['parameters']\n\n\nMODELS = [ActionExecutionDB]\n", "path": "st2common/st2common/models/db/execution.py"}]}