summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxmo-odoo <xmo@odoo.com>2017-08-10 14:51:27 +0200
committerRaphael Collet <rco@odoo.com>2017-08-10 14:51:27 +0200
commitd140f0ef0e7d9cb3eb4a6ad344ae761936a0a288 (patch)
treef0e323657b56fff5ea288c7278a75b48d6a29b48
parent354539942d29acc4cfefdb8851ed18ff1bdd4b63 (diff)
[FIX] prefetch issues on computed/related fields (#18644)
When deciding to prefetch records (getting records from the cache with no value for the field being fetched), if the field was computed `determine_value` would just get all records, not limited by the normal prefetch limit; for large recordsets this would generate gigantic prefetch lists for records we may not need at all. Fix by applying the `PREFETCH_MAX` limit to records from the cache as is done in `_prefetch_field`. Complementarily, when traversing related fields the prefetch environment would be lost and every record would get an empty prefetch environment, so the values would ultimately be read one by one. Example: select (search) 1000 product.product records, access a related field (e.g. categ_id) in a loop, on the first iteration the system would first read 1000 templates, then it would read each categ_id individually, resulting in >1000 SQL queries rather than the ~2 we would expect. Fixes #18511
-rw-r--r--odoo/fields.py3
-rw-r--r--odoo/models.py17
2 files changed, 10 insertions, 10 deletions
diff --git a/odoo/fields.py b/odoo/fields.py
index ee9547ca774..745e5effb62 100644
--- a/odoo/fields.py
+++ b/odoo/fields.py
@@ -565,7 +565,7 @@ class Field(object):
""" Traverse the fields of the related field `self` except for the last
one, and return it as a pair `(last_record, last_field)`. """
for name in self.related[:-1]:
- record = record[name][:1]
+ record = record[name][:1].with_prefetch(record._prefetch)
return record, self.related_field
def _compute_related(self, records):
@@ -978,6 +978,7 @@ class Field(object):
self.compute_value(record)
else:
recs = record._in_cache_without(self)
+ recs = recs.with_prefetch(record._prefetch)
self.compute_value(recs)
else:
diff --git a/odoo/models.py b/odoo/models.py
index 5eeef77ef4e..f58ed393eb2 100644
--- a/odoo/models.py
+++ b/odoo/models.py
@@ -3058,10 +3058,6 @@ class BaseModel(object):
else:
records &= self._in_cache_without(f)
- # prefetch at most PREFETCH_MAX records
- if len(records) > PREFETCH_MAX:
- records = records[:PREFETCH_MAX] | self
-
# fetch records with read()
assert self in records and field in fs
records = records.with_prefetch(self._prefetch)
@@ -5232,13 +5228,16 @@ class BaseModel(object):
return RecordCache(self)
@api.model
- def _in_cache_without(self, field):
- """ Make sure ``self`` is present in cache (for prefetching), and return
- the records of model ``self`` in cache that have no value for ``field``
- (:class:`Field` instance).
+ def _in_cache_without(self, field, limit=PREFETCH_MAX):
+ """ Return records to prefetch that have no value in cache for ``field``
+ (:class:`Field` instance), including ``self``.
+ Return at most ``limit`` records.
"""
ids = filter(None, self._prefetch[self._name] - set(self.env.cache[field]))
- return self.browse(ids)
+ recs = self.browse(ids)
+ if limit and len(recs) > limit:
+ recs = self + (recs - self)[:(limit - len(self))]
+ return recs
@api.model
def refresh(self):