diff options
| author | xmo-odoo <xmo@odoo.com> | 2017-08-10 14:51:27 +0200 |
|---|---|---|
| committer | Raphael Collet <rco@odoo.com> | 2017-08-10 14:51:27 +0200 |
| commit | d140f0ef0e7d9cb3eb4a6ad344ae761936a0a288 (patch) | |
| tree | f0e323657b56fff5ea288c7278a75b48d6a29b48 | |
| parent | 354539942d29acc4cfefdb8851ed18ff1bdd4b63 (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.py | 3 | ||||
| -rw-r--r-- | odoo/models.py | 17 |
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): |
