Skip to content

Commit a7582ae

Browse files
committed
Get rid if automatic auto conversion / de-referencing of embedded fields
inside the base mongoengine document class. This process is very slow and adds a tons of overhead since it calls "to_python()" on every model field even if the field doesn't need to be referenced. With this approach we manually de-reference only the fields which need to be dereferenced / converted. The most ideal and faster approach would be to work directly with dictionaries as returned by pymongo (this way we could avoid the whole overhead), but this is a compromise.
1 parent a4a37b9 commit a7582ae

File tree

8 files changed

+75
-3
lines changed

8 files changed

+75
-3
lines changed

st2common/st2common/models/db/__init__.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,8 @@ def insert(self, instance):
487487
def add_or_update(self, instance, validate=True, undo_dict_field_escape=True):
488488
instance.save(validate=validate)
489489
if undo_dict_field_escape:
490-
return self._undo_dict_field_escape(instance)
490+
instance = self._undo_dict_field_escape(instance)
491+
instance.id = str(instance.id)
491492
return instance
492493

493494
def update(self, instance, **kwargs):
@@ -609,8 +610,11 @@ def _process_as_pymongo_queryset(self, queryset, as_pymongo=False):
609610
for item in result:
610611
if '_id' in item:
611612
item['id'] = str(item.pop('_id'))
612-
# TODO: Also avoid expensive auto conversion since it's not needed in most cases
613-
model_db = self.model(__auto_convert=True, **item)
613+
# NOTE: Disabling auto_convert speeds it up by 50%
614+
# Derefernces only need to happen where we use EmbeddedDocumentField which is only in
615+
# a few places
616+
model_db = self.model(__auto_convert=False, **item)
617+
model_db.id = str(model_db.id)
614618
models_result.append(model_db)
615619

616620
return models_result

st2common/st2common/models/db/action.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,15 @@ def __init__(self, *args, **values):
9494
self.ref = self.get_reference().ref
9595
self.uid = self.get_uid()
9696

97+
# Manualy de-reference EmbeddedDocumentField fields to avoid overhead of de-referencing all
98+
# the fields inside the base Document class constructor when __auto_convert is True.
99+
# This approach means we need to update this code each time we add new
100+
# EmbeddedDocumentField (which we should avoid anyway for performance reasons)
101+
stormbase.TagsMixin.__init__(self)
102+
103+
if self.notify:
104+
self.notify = self._fields['notify'].to_python(self.notify)
105+
97106
def is_workflow(self):
98107
"""
99108
Return True if this action is a workflow, False otherwise.

st2common/st2common/models/db/liveaction.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,16 @@ class LiveActionDB(stormbase.StormFoundationDB):
8787
]
8888
}
8989

90+
def __init__(self, *args, **kwargs):
91+
super(LiveActionDB, self).__init__(*args, **kwargs)
92+
93+
# Manualy de-reference EmbeddedDocumentField fields to avoid overhead of de-referencing all
94+
# the fields inside the base Document class constructor when __auto_convert is True.
95+
# This approach means we need to update this code each time we add new
96+
# EmbeddedDocumentField (which we should avoid anyway for performance reasons)
97+
if self.notify:
98+
self.notify = self._fields['notify'].to_python(self.notify)
99+
90100
def mask_secrets(self, value):
91101
from st2common.util import action_db
92102

st2common/st2common/models/db/rule.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,18 @@ def __init__(self, *args, **values):
159159
self.ref = self.get_reference().ref
160160
self.uid = self.get_uid()
161161

162+
# Manualy de-reference EmbeddedDocumentField fields to avoid overhead of de-referencing all
163+
# the fields inside the base Document class constructor when __auto_convert is True.
164+
# This approach means we need to update this code each time we add new
165+
# EmbeddedDocumentField (which we should avoid anyway for performance reasons)
166+
stormbase.TagsMixin.__init__(self)
167+
168+
if self.type:
169+
self.type = self._fields['type'].to_python(self.type)
170+
171+
if self.action:
172+
self.action = self._fields['action'].to_python(self.action)
173+
162174

163175
rule_access = MongoDBAccess(RuleDB)
164176
rule_type_access = MongoDBAccess(RuleTypeDB)

st2common/st2common/models/db/rule_enforcement.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,15 @@ class RuleEnforcementDB(stormbase.StormFoundationDB, stormbase.TagsMixin):
8080
def __init__(self, *args, **values):
8181
super(RuleEnforcementDB, self).__init__(*args, **values)
8282

83+
# Manualy de-reference EmbeddedDocumentField fields to avoid overhead of de-referencing all
84+
# the fields inside the base Document class constructor when __auto_convert is True.
85+
# This approach means we need to update this code each time we add new
86+
# EmbeddedDocumentField (which we should avoid anyway for performance reasons)
87+
stormbase.TagsMixin.__init__(self)
88+
89+
if self.rule:
90+
self.rule = self._fields['rule'].to_python(self.rule)
91+
8392
# Set status to succeeded for old / existing RuleEnforcementDB which predate status field
8493
status = getattr(self, 'status', None)
8594
failure_reason = getattr(self, 'failure_reason', None)

st2common/st2common/models/db/stormbase.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,12 @@ class TagsMixin(object):
165165
"""
166166
tags = me.ListField(field=me.EmbeddedDocumentField(TagField))
167167

168+
def __init__(self):
169+
# Manualy de-reference EmbeddedDocumentField fields to avoid overhead of de-referencing all
170+
# the fields inside the base Document class constructor when __auto_convert is True
171+
if self.tags:
172+
self.tags = self._fields['tags'].to_python(self.tags)
173+
168174
@classmethod
169175
def get_indexes(cls):
170176
return ['tags.name', 'tags.value']

st2common/st2common/models/db/trace.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,22 @@ def __init__(self, *args, **values):
9494
super(TraceDB, self).__init__(*args, **values)
9595
self.uid = self.get_uid()
9696

97+
# Manualy de-reference EmbeddedDocumentField fields to avoid overhead of de-referencing all
98+
# the fields inside the base Document class constructor when __auto_convert is True.
99+
# This approach means we need to update this code each time we add new
100+
# EmbeddedDocumentField (which we should avoid anyway for performance reasons)
101+
if self.trigger_instances:
102+
self.trigger_instances = \
103+
self._fields['trigger_instances'].to_python(self.trigger_instances)
104+
105+
if self.rules:
106+
self.rules = \
107+
self._fields['rules'].to_python(self.rules)
108+
109+
if self.action_executions:
110+
self.action_executions = \
111+
self._fields['action_executions'].to_python(self.action_executions)
112+
97113
def get_uid(self):
98114
parts = []
99115
parts.append(self.RESOURCE_TYPE)

st2common/st2common/models/db/trigger.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ def __init__(self, *args, **values):
6464
# pylint: disable=no-member
6565
self.uid = self.get_uid()
6666

67+
# Manualy de-reference EmbeddedDocumentField fields to avoid overhead of de-referencing all
68+
# the fields inside the base Document class constructor when __auto_convert is True.
69+
# This approach means we need to update this code each time we add new
70+
# EmbeddedDocumentField (which we should avoid anyway for performance reasons)
71+
stormbase.TagsMixin.__init__(self)
72+
6773

6874
class TriggerDB(stormbase.StormBaseDB, stormbase.ContentPackResourceMixin,
6975
stormbase.UIDFieldMixin):

0 commit comments

Comments
 (0)