Skip to content

Commit 3e4f017

Browse files
committed
[FIX] recompute_fields: get ids in batches
If no ids are given to recompute, the util will fetch all ids in the target table and then recompute in chunks. Fetching all the ids itself can cause a memory error if the table is too large. Using a named cursor with a limit of 1M records to fetch can eliminate this possibility. closes #303 Signed-off-by: Christophe Simonis (chs) <[email protected]>
1 parent 5d6a22e commit 3e4f017

File tree

1 file changed

+19
-7
lines changed

1 file changed

+19
-7
lines changed

src/util/orm.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from contextlib import contextmanager
1515
from functools import wraps
1616
from itertools import chain
17-
from operator import itemgetter
1817
from textwrap import dedent
1918

2019
try:
@@ -43,7 +42,7 @@
4342
from .exceptions import MigrationError
4443
from .helpers import table_of_model
4544
from .misc import chunks, log_progress, version_between, version_gte
46-
from .pg import column_exists, get_columns
45+
from .pg import column_exists, format_query, get_columns, named_cursor
4746

4847
# python3 shims
4948
try:
@@ -281,18 +280,31 @@ def recompute_fields(cr, model, fields, ids=None, logger=_logger, chunk_size=256
281280
assert strategy in {"flush", "commit", "auto"}
282281
Model = env(cr)[model] if isinstance(model, basestring) else model
283282
model = Model._name
283+
284+
def get_ids():
285+
if ids is not None:
286+
for id_ in ids:
287+
yield id_
288+
else:
289+
with named_cursor(cr, itersize=2**20) as ncr:
290+
ncr.execute(format_query(cr, "SELECT id FROM {t} ORDER BY id", t=table_of_model(cr, model)))
291+
for (id_,) in ncr:
292+
yield id_
293+
284294
if ids is None:
285-
cr.execute('SELECT id FROM "%s"' % table_of_model(cr, model))
286-
ids = tuple(map(itemgetter(0), cr.fetchall()))
295+
cr.execute(format_query(cr, "SELECT COUNT(id) FROM {t}", t=table_of_model(cr, model)))
296+
(count,) = cr.fetchone()
297+
else:
298+
count = len(ids)
287299

288300
if strategy == "auto":
289-
big_table = len(ids) > BIG_TABLE_THRESHOLD
301+
big_table = count > BIG_TABLE_THRESHOLD
290302
any_tracked_field = any(getattr(Model._fields[f], _TRACKING_ATTR, False) for f in fields)
291303
strategy = "commit" if big_table and any_tracked_field else "flush"
292304

293-
size = (len(ids) + chunk_size - 1) / chunk_size
305+
size = (count + chunk_size - 1) / chunk_size
294306
qual = "%s %d-bucket" % (model, chunk_size) if chunk_size != 1 else model
295-
for subids in log_progress(chunks(ids, chunk_size, list), logger, qualifier=qual, size=size):
307+
for subids in log_progress(chunks(get_ids(), chunk_size, list), logger, qualifier=qual, size=size):
296308
records = Model.browse(subids)
297309
for field_name in fields:
298310
field = records._fields[field_name]

0 commit comments

Comments
 (0)