[IMP] core: make compute order deterministic
CRM install query count can vary from one execution to another, leading to difficulties when analysing performances evolution. The main reason for this is that some compute methods were called in different order. Even if compute order shouldn't have any effect on the final result, making it well defined will help finding other causes of non-determinism. The initial observation was that sorting Environment.fields_to_compute leads to a fixed number of query when installing crm. The main cause of non-determinisim is the usage of `set` impacting Field.compute_value and BaseModel._modified_triggers. Transforming all these `set` to `OrderedSet` solves the problem. The query count is now deterministic when installing a database from scratch, but not when updating a database with -i crm. OrderedSet is also slightly optimised by using a dict instead of an closes odoo/odoo#68692 Ordereddict: dict order is deterministic since python3.6 Signed-off-by: Xavier Dollé (xdo) <xdo@odoo.com>
This commit is contained in:
+4
-4
@@ -139,7 +139,7 @@ def trigger_tree_merge(node1, node2):
|
||||
""" Merge two trigger trees. """
|
||||
for key, val in node2.items():
|
||||
if key is None:
|
||||
node1.setdefault(None, set())
|
||||
node1.setdefault(None, OrderedSet())
|
||||
node1[None].update(val)
|
||||
else:
|
||||
node1.setdefault(key, {})
|
||||
@@ -3924,9 +3924,9 @@ Fields:
|
||||
quote = '"{}"'.format
|
||||
|
||||
# insert rows
|
||||
ids = [] # ids of created records
|
||||
other_fields = set() # non-column fields
|
||||
translated_fields = set() # translated fields
|
||||
ids = [] # ids of created records
|
||||
other_fields = OrderedSet() # non-column fields
|
||||
translated_fields = OrderedSet() # translated fields
|
||||
|
||||
for data in data_list:
|
||||
# determine column values
|
||||
|
||||
@@ -312,7 +312,7 @@ class Registry(Mapping):
|
||||
# dependencies of custom fields may not exist; ignore that case
|
||||
exceptions = (Exception,) if field.base_field.manual else ()
|
||||
with ignore(*exceptions):
|
||||
dependencies[field] = set(field.resolve_depends(self))
|
||||
dependencies[field] = OrderedSet(field.resolve_depends(self))
|
||||
|
||||
# determine transitive dependencies
|
||||
def transitive_dependencies(field, seen=[]):
|
||||
@@ -339,7 +339,7 @@ class Registry(Mapping):
|
||||
tree = triggers
|
||||
for label in reversed(path):
|
||||
tree = tree.setdefault(label, {})
|
||||
tree.setdefault(None, set()).add(field)
|
||||
tree.setdefault(None, OrderedSet()).add(field)
|
||||
|
||||
return triggers
|
||||
|
||||
|
||||
+18
-1
@@ -11,6 +11,7 @@ import datetime
|
||||
import hmac as hmac_lib
|
||||
import hashlib
|
||||
import io
|
||||
import itertools
|
||||
import os
|
||||
import pickle as pickle_
|
||||
import re
|
||||
@@ -1013,19 +1014,35 @@ class StackMap(MutableMapping):
|
||||
class OrderedSet(MutableSet):
|
||||
""" A set collection that remembers the elements first insertion order. """
|
||||
__slots__ = ['_map']
|
||||
|
||||
def __init__(self, elems=()):
|
||||
self._map = OrderedDict((elem, None) for elem in elems)
|
||||
self._map = dict.fromkeys(elems)
|
||||
|
||||
def __contains__(self, elem):
|
||||
return elem in self._map
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._map)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._map)
|
||||
|
||||
def add(self, elem):
|
||||
self._map[elem] = None
|
||||
|
||||
def discard(self, elem):
|
||||
self._map.pop(elem, None)
|
||||
|
||||
def update(self, elems):
|
||||
self._map.update(zip(elems, itertools.repeat(None)))
|
||||
|
||||
def difference_update(self, elems):
|
||||
for elem in elems:
|
||||
self.discard(elem)
|
||||
|
||||
def __repr__(self):
|
||||
return f'{type(self).__name__}({list(self)!r})'
|
||||
|
||||
|
||||
class LastOrderedSet(OrderedSet):
|
||||
""" A set collection that remembers the elements last insertion order. """
|
||||
|
||||
Reference in New Issue
Block a user