[IMP] fields: use a "lazy" cursor to compute attribute digits

Borrowing a cursor each time you access `field.digits` may be costly, because
of the connection reset.  Moreover, in most cases, the cursor is not used at
all, since the decimal precision are kept in cache.

For the installation of module `product` with its demo data, the number of
cursor allocations was reduced from ~1500 to about 50!
This commit is contained in:
Raphael Collet
2016-01-29 09:37:27 +01:00
parent 6f4e7867f0
commit f0646cb51b
3 changed files with 43 additions and 11 deletions
+3 -2
View File
@@ -30,6 +30,7 @@ import logging
import pytz
import xmlrpclib
from openerp.sql_db import LazyCursor
from openerp.tools import float_round, frozendict, html_sanitize, ustr, OrderedSet
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as DATE_FORMAT
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT
@@ -1070,7 +1071,7 @@ class Float(Field):
@property
def digits(self):
if callable(self._digits):
with fields._get_cursor() as cr:
with LazyCursor() as cr:
return self._digits(cr)
else:
return self._digits
@@ -1882,7 +1883,7 @@ class Id(Field):
raise TypeError("field 'id' cannot be assigned")
# imported here to avoid dependency cycle issues
from openerp import SUPERUSER_ID, registry
from openerp import SUPERUSER_ID
from .exceptions import Warning, AccessError, MissingError
from .models import BaseModel, MAGIC_COLUMNS
from .osv import fields
+6 -9
View File
@@ -43,21 +43,18 @@ import pytz
import re
import xmlrpclib
from operator import itemgetter
from contextlib import contextmanager
from psycopg2 import Binary
import openerp
import openerp.tools as tools
from openerp.sql_db import LazyCursor
from openerp.tools.translate import _
from openerp.tools import float_repr, float_round, frozendict, html_sanitize
import simplejson
from openerp import SUPERUSER_ID, registry
from openerp import SUPERUSER_ID
@contextmanager
def _get_cursor():
# yield a valid cursor from any environment or create a new one if none found
with registry().cursor() as cr:
yield cr
# deprecated; kept for backward compatibility only
_get_cursor = LazyCursor
EMPTY_DICT = frozendict()
@@ -396,7 +393,7 @@ class float(_column):
@property
def digits(self):
if self._digits_compute:
with _get_cursor() as cr:
with LazyCursor() as cr:
return self._digits_compute(cr)
else:
return self._digits
@@ -1323,7 +1320,7 @@ class function(_column):
@property
def digits(self):
if self._digits_compute:
with _get_cursor() as cr:
with LazyCursor() as cr:
return self._digits_compute(cr)
else:
return self._digits
+34
View File
@@ -443,6 +443,40 @@ class TestCursor(Cursor):
self.execute("ROLLBACK TO SAVEPOINT test_cursor")
self.execute("SAVEPOINT test_cursor")
class LazyCursor(object):
""" A proxy object to a cursor. The cursor itself is allocated only if it is
needed. This class is useful for cached methods, that use the cursor
only in the case of a cache miss.
"""
def __init__(self, dbname=None):
self._dbname = dbname
self._cursor = None
self._depth = 0
@property
def dbname(self):
return self._dbname or threading.currentThread().dbname
def __getattr__(self, name):
cr = self._cursor
if cr is None:
from openerp import registry
cr = self._cursor = registry(self.dbname).cursor()
for _ in xrange(self._depth):
cr.__enter__()
return getattr(cr, name)
def __enter__(self):
self._depth += 1
if self._cursor is not None:
self._cursor.__enter__()
return self
def __exit__(self, exc_type, exc_value, traceback):
self._depth -= 1
if self._cursor is not None:
self._cursor.__exit__(exc_type, exc_value, traceback)
class PsycoConnection(psycopg2.extensions.connection):
pass