[rqlrewrite/schema] Avoid parsing computed relations for each query

The RQLRelationRewriter is instanciated for each RQL query, it should avoid parsing computed relations formula by using a cache, which seems rightly located on the instance's schema.

This brings a huge performance boost to some pages on application with a few computed relations (x4 observed on a client app).

Kudos to Adrien, David and Sylvain.

Closes #17059828

authorArthur Lutz <arthur.lutz@logilab.fr>
changeset0669e8661439
branch3.24
phasedraft
hiddenno
parent revision#7c7d58cc5c1f [cwctl] do only clean static data dir content (closes #17069762)
child revision<not specified>
files modified by this revision
cubicweb/rqlrewrite.py
cubicweb/schema.py
# HG changeset patch
# User Arthur Lutz <arthur.lutz@logilab.fr>
# Date 1488388069 -3600
# Wed Mar 01 18:07:49 2017 +0100
# Branch 3.24
# Node ID 0669e86614393994b30b5bab450c892fdcb8a685
# Parent 7c7d58cc5c1f2f18eac46f37c6f831a1d0d33162
[rqlrewrite/schema] Avoid parsing computed relations for each query

The RQLRelationRewriter is instanciated for each RQL query, it should
avoid parsing computed relations formula by using a cache, which seems rightly
located on the instance's schema.

This brings a *huge* performance boost to some pages on application with a few
computed relations (x4 observed on a client app).

Kudos to Adrien, David and Sylvain.

Closes #17059828

diff --git a/cubicweb/rqlrewrite.py b/cubicweb/rqlrewrite.py
@@ -900,26 +900,22 @@
1      """Insert some rql snippets into another rql syntax tree, replacing computed
2      relations by their associated rule.
3 
4      This class *isn't thread safe*.
5      """
6 -    def __init__(self, session):
7 -        super(RQLRelationRewriter, self).__init__(session)
8 -        self.rules = {}
9 -        for rschema in self.schema.iter_computed_relations():
10 -            self.rules[rschema.type] = RRQLExpression(rschema.rule)
11 
12      def rewrite(self, union, kwargs=None):
13          self.kwargs = kwargs
14          self.removing_ambiguity = False
15          self.existingvars = None
16          self.pending_keys = None
17 +        rules = self.schema.rules_rqlexpr_mapping
18          for relation in union.iget_nodes(n.Relation):
19 -            if relation.r_type in self.rules:
20 +            if relation.r_type in rules:
21                  self.select = relation.stmt
22                  self.solutions = solutions = self.select.solutions[:]
23 -                self.current_expr = self.rules[relation.r_type]
24 +                self.current_expr = rules[relation.r_type]
25                  self._insert_scope = relation.scope
26                  self.rewritten = {}
27                  lhs, rhs = relation.get_variable_parts()
28                  varmap = {lhs.name: 'S', rhs.name: 'O'}
29                  self.init_from_varmap(tuple(sorted(varmap.items())))
diff --git a/cubicweb/schema.py b/cubicweb/schema.py
@@ -1002,10 +1002,20 @@
30          rschema = self.add_relation_type(ybo.RelationType('identity'))
31          rschema.final = False
32 
33      etype_name_re = r'[A-Z][A-Za-z0-9]*[a-z]+[A-Za-z0-9]*$'
34 
35 +    @cachedproperty
36 +    def rules_rqlexpr_mapping(self):
37 +        """Return a dictionary mapping rtype to RRQLExpression for computed
38 +        relations.
39 +        """
40 +        rules = {}
41 +        for rschema in self.iter_computed_relations():
42 +            rules[rschema.type] = RRQLExpression(rschema.rule)
43 +        return rules
44 +
45      def add_entity_type(self, edef):
46          edef.name = str(edef.name)
47          edef.name = bw_normalize_etype(edef.name)
48          if not re.match(self.etype_name_re, edef.name):
49              raise BadSchemaDefinition(
@@ -1142,10 +1152,12 @@
50 
51      def rebuild_infered_relations(self):
52          super(CubicWebSchema, self).rebuild_infered_relations()
53          self.finalize_computed_attributes()
54          self.finalize_computed_relations()
55 +        # remove @cachedproperty cache
56 +        self.__dict__.pop('rules_rqlexpr_mapping', None)
57 
58 
59  # additional cw specific constraints ###########################################
60 
61  @monkeypatch(BaseConstraint)