[IMP] base: speedup routing map
When generating the routing map, `Rule.add` will call
`Rule._compile_builder` twice, representing almost one third of the
routing map generation time. `_compile_builder` is actually stored
in `Rule._build` and `Rule._build_unknown` for latter use, in the
`Rule.build` method.
The `Rule.build` method is used to transform a rule, lets say
`/forum/<model("forum.forum"):forum>` in `/forum/basics-of-gardening-2`
Even if a deeper investigation could be interresting, it looks like it
is only used for `is_frontend_multilang` routes and `_enumerate_pages`,
used in the `sitemap` and `search_pages`.
The proposed solution si to make this part lazy, in order to call
_compile_builder on demand, once per rule. This will speedup the initial
routing map generation and postpone the heavy work when we need it,
only for the part we need most of the time.
On a database with all modules installed, generation of the routing map:
Before: ~600 ms
After: ~200 ms
An alternative implementation was also overriding the Rule.build method
instead of having a callable LazyCompiledBuilder, this implementation
was choosen since it is less dependant off the werkzeug implementation.
closes odoo/odoo#120542
Signed-off-by: Xavier Dollé (xdo) <xdo@odoo.com>
This commit is contained in:
@@ -79,6 +79,42 @@ class SignedIntConverter(NumberConverter):
|
||||
num_convert = int
|
||||
|
||||
|
||||
class LazyCompiledBuilder:
|
||||
def __init__(self, rule, _compile_builder, append_unknown):
|
||||
self.rule = rule
|
||||
self._callable = None
|
||||
self._compile_builder = _compile_builder
|
||||
self._append_unknown = append_unknown
|
||||
|
||||
def __get__(self, *args):
|
||||
# Rule.compile will actually call
|
||||
#
|
||||
# self._build = self._compile_builder(False).__get__(self, None)
|
||||
# self._build_unknown = self._compile_builder(True).__get__(self, None)
|
||||
#
|
||||
# meaning the _build and _build unkown will contain _compile_builder().__get__(self, None).
|
||||
# This is why this override of __get__ is needed.
|
||||
return self
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
if self._callable is None:
|
||||
self._callable = self._compile_builder(self._append_unknown).__get__(self.rule, None)
|
||||
del self.rule
|
||||
del self._compile_builder
|
||||
del self._append_unknown
|
||||
return self._callable(*args, **kwargs)
|
||||
|
||||
|
||||
class FasterRule(werkzeug.routing.Rule):
|
||||
"""
|
||||
_compile_builder is a major part of the routing map generation and rules
|
||||
are actually not build so often.
|
||||
This classe makes calls to _compile_builder lazy
|
||||
"""
|
||||
def _compile_builder(self, append_unknown=True):
|
||||
return LazyCompiledBuilder(self, super()._compile_builder, append_unknown)
|
||||
|
||||
|
||||
class IrHttp(models.AbstractModel):
|
||||
_name = 'ir.http'
|
||||
_description = "HTTP Routing"
|
||||
@@ -206,7 +242,7 @@ class IrHttp(models.AbstractModel):
|
||||
routing = submap(endpoint.routing, ROUTING_KEYS)
|
||||
if routing['methods'] is not None and 'OPTIONS' not in routing['methods']:
|
||||
routing['methods'] = routing['methods'] + ['OPTIONS']
|
||||
rule = werkzeug.routing.Rule(url, endpoint=endpoint, **routing)
|
||||
rule = FasterRule(url, endpoint=endpoint, **routing)
|
||||
rule.merge_slashes = False
|
||||
routing_map.add(rule)
|
||||
cls._routing_map[key] = routing_map
|
||||
|
||||
Reference in New Issue
Block a user