Commit d70587e3 authored by Julien Muchembled's avatar Julien Muchembled

Cleanup API of TransactionalVariable and read-only cache

- New readOnlyTransactionCache context manager replacing
  enableReadOnlyTransactionCache and disableReadOnlyTransactionCache
- Drop compatibility code that tolerate a dummy context to be passed to
  getReadOnlyTransactionCache and getTransactionalVariable.
parent 8995b645
...@@ -32,7 +32,7 @@ from Products.CMFCore.utils import getToolByName ...@@ -32,7 +32,7 @@ from Products.CMFCore.utils import getToolByName
from Products.ERP5Type import Permissions, PropertySheet from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5.Document.ExternalSource import ExternalSource from Products.ERP5.Document.ExternalSource import ExternalSource
from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5Type.Cache import getReadOnlyTransactionCache, enableReadOnlyTransactionCache from Products.ERP5Type.Cache import getReadOnlyTransactionCache
# IMAP imports # IMAP imports
import imaplib import imaplib
...@@ -442,18 +442,13 @@ class EmailReader(ExternalSource): ...@@ -442,18 +442,13 @@ class EmailReader(ExternalSource):
Returns the list of folders of the current server Returns the list of folders of the current server
XXX Add read only transaction cache XXX Add read only transaction cache
""" """
enabled = (getReadOnlyTransactionCache(self) is not None) cache = getReadOnlyTransactionCache()
if not enabled:
enableReadOnlyTransactionCache(self)
cache = getReadOnlyTransactionCache(self)
if cache is not None: if cache is not None:
key = ('getMessageFolderList', self) key = ('getMessageFolderList', self)
try: try:
return cache[key] return cache[key]
except KeyError: except KeyError:
pass pass
else:
raise "No Conversion Cache" # XXX - Implement this better
server = self._getMailServer() server = self._getMailServer()
if server is None: return () if server is None: return ()
...@@ -473,18 +468,13 @@ class EmailReader(ExternalSource): ...@@ -473,18 +468,13 @@ class EmailReader(ExternalSource):
break things. An interactor is required to clear break things. An interactor is required to clear
the variable the variable
""" """
enabled = (getReadOnlyTransactionCache(self) is not None) cache = getReadOnlyTransactionCache()
if not enabled:
enableReadOnlyTransactionCache(self)
cache = getReadOnlyTransactionCache(self)
if cache is not None: if cache is not None:
key = ('_getMailServer', self) key = ('_getMailServer', self)
try: try:
return cache[key] return cache[key]
except KeyError: except KeyError:
pass pass
else:
raise "No Conversion Cache" # XXX - Implement this better
# No server defined # No server defined
if not self.getURLServer(): return None if not self.getURLServer(): return None
......
28 29
\ No newline at end of file \ No newline at end of file
...@@ -617,7 +617,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -617,7 +617,7 @@ class BusinessProcess(Path, XMLObject):
""" """
remaining_trade_phase_list = [] remaining_trade_phase_list = []
trade_state = business_link.getSuccessor() trade_state = business_link.getSuccessor()
tv = getTransactionalVariable(self) tv = getTransactionalVariable()
# We might need a key which depends on the explanation # We might need a key which depends on the explanation
key = 'BusinessProcess_predecessor_successor_%s' % self.getRelativeUrl() key = 'BusinessProcess_predecessor_successor_%s' % self.getRelativeUrl()
predecessor_successor_dict = tv.get(key, None) predecessor_successor_dict = tv.get(key, None)
......
...@@ -31,7 +31,7 @@ from AccessControl import ClassSecurityInfo ...@@ -31,7 +31,7 @@ from AccessControl import ClassSecurityInfo
from Products.ERP5Type.Core.Predicate import Predicate from Products.ERP5Type.Core.Predicate import Predicate
from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5Type import Permissions, PropertySheet from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5Type.Cache import getReadOnlyTransactionCache, enableReadOnlyTransactionCache, disableReadOnlyTransactionCache from Products.ERP5Type.Cache import readOnlyTransactionCache
class ContributionPredicate(Predicate, XMLObject): class ContributionPredicate(Predicate, XMLObject):
...@@ -94,13 +94,9 @@ class ContributionPredicate(Predicate, XMLObject): ...@@ -94,13 +94,9 @@ class ContributionPredicate(Predicate, XMLObject):
tested_base_category_list] tested_base_category_list]
# Test category memberships. Enable the read-only transaction cache # Test category memberships. Enable the read-only transaction cache
# temporarily, if not enabled, because this part is strictly read-only, # because this part is strictly read-only, and context.isMemberOf
# and context.isMemberOf is very expensive, when the category list has # is very expensive when the category list has many items.
# many items. with readOnlyTransactionCache():
enabled = getReadOnlyTransactionCache() is not None
try:
if not enabled:
enableReadOnlyTransactionCache()
for c in membership_criterion_category_list: for c in membership_criterion_category_list:
bc = c.split('/', 1)[0] bc = c.split('/', 1)[0]
if (bc not in tested_base_category) and \ if (bc not in tested_base_category) and \
...@@ -115,9 +111,6 @@ class ContributionPredicate(Predicate, XMLObject): ...@@ -115,9 +111,6 @@ class ContributionPredicate(Predicate, XMLObject):
elif (bc in membership_criterion_base_category_list): elif (bc in membership_criterion_base_category_list):
tested_base_category[bc] = tested_base_category[bc] or \ tested_base_category[bc] = tested_base_category[bc] or \
context.isMemberOf(c) context.isMemberOf(c)
finally:
if not enabled:
disableReadOnlyTransactionCache()
result = result and (0 not in tested_base_category.values()) result = result and (0 not in tested_base_category.values())
# Test method calls # Test method calls
......
...@@ -755,18 +755,17 @@ class BuilderMixin(XMLObject, Amount, Predicate): ...@@ -755,18 +755,17 @@ class BuilderMixin(XMLObject, Amount, Predicate):
return obj return obj
def _isUpdated(self, obj, level): def _isUpdated(self, obj, level):
tv = getTransactionalVariable(self) tv = getTransactionalVariable()
return level in tv['builder_processed_list'].get(obj, []) return level in tv['builder_processed_list'].get(obj, ())
def _setUpdated(self, obj, level): def _setUpdated(self, obj, level):
tv = getTransactionalVariable(self) tv = getTransactionalVariable()
if tv.get('builder_processed_list', None) is None: if 'builder_processed_list' not in tv:
self._resetUpdated() self._resetUpdated()
tv['builder_processed_list'][obj] = \ tv['builder_processed_list'].setdefault(obj, []).append(level)
tv['builder_processed_list'].get(obj, []) + [level]
def _resetUpdated(self): def _resetUpdated(self):
tv = getTransactionalVariable(self) tv = getTransactionalVariable()
tv['builder_processed_list'] = {} tv['builder_processed_list'] = {}
# for backward compatibilities. # for backward compatibilities.
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
############################################################################## ##############################################################################
import string import string
from contextlib import contextmanager
from time import time from time import time
from AccessControl import allow_class, ClassSecurityInfo from AccessControl import allow_class, ClassSecurityInfo
from Acquisition import aq_base from Acquisition import aq_base
...@@ -36,7 +37,7 @@ from CachePlugins.BaseCache import CachedMethodError ...@@ -36,7 +37,7 @@ from CachePlugins.BaseCache import CachedMethodError
from persistent import Persistent from persistent import Persistent
from zLOG import LOG, WARNING from zLOG import LOG, WARNING
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable, _MARKER from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
from Products.ERP5Type.Utils import simple_decorator from Products.ERP5Type.Utils import simple_decorator
from warnings import warn from warnings import warn
...@@ -291,20 +292,22 @@ allow_class(CachingMethod) ...@@ -291,20 +292,22 @@ allow_class(CachingMethod)
# TransactionCache is a cache per transaction. The purpose of this cache is # TransactionCache is a cache per transaction. The purpose of this cache is
# to accelerate some heavy read-only operations. Note that this must not be # to accelerate some heavy read-only operations. Note that this must not be
# enabled when a transaction may modify ZODB objects. # enabled when a transaction may modify ZODB objects.
def getReadOnlyTransactionCache(context=_MARKER): def getReadOnlyTransactionCache():
"""Get the transaction cache. """Get the transaction cache.
""" """
return getTransactionalVariable(context).get('read_only_transaction_cache') return getTransactionalVariable().get('read_only_transaction_cache')
def enableReadOnlyTransactionCache(context=_MARKER): @contextmanager
"""Enable the transaction cache. def readOnlyTransactionCache():
""" tv = getTransactionalVariable()
getTransactionalVariable(context)['read_only_transaction_cache'] = {} if 'read_only_transaction_cache' in tv:
yield
def disableReadOnlyTransactionCache(context=_MARKER): else:
"""Disable the transaction cache. tv['read_only_transaction_cache'] = {}
""" try:
getTransactionalVariable(context).pop('read_only_transaction_cache', None) yield
finally:
del tv['read_only_transaction_cache']
######################################################## ########################################################
## Old global cache functions ## ## Old global cache functions ##
......
...@@ -40,7 +40,7 @@ from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter ...@@ -40,7 +40,7 @@ from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter
from Products.ERP5Type.Document import newTempBase from Products.ERP5Type.Document import newTempBase
from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5Type.Utils import convertToUpperCase from Products.ERP5Type.Utils import convertToUpperCase
from Products.ERP5Type.Cache import getReadOnlyTransactionCache, enableReadOnlyTransactionCache, disableReadOnlyTransactionCache from Products.ERP5Type.Cache import readOnlyTransactionCache
from Products.ZSQLCatalog.SQLCatalog import SQLQuery from Products.ZSQLCatalog.SQLCatalog import SQLQuery
from Products.ERP5Type.Globals import PersistentMapping from Products.ERP5Type.Globals import PersistentMapping
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
...@@ -150,13 +150,9 @@ class Predicate(XMLObject): ...@@ -150,13 +150,9 @@ class Predicate(XMLObject):
tested_base_category_list] tested_base_category_list]
# Test category memberships. Enable the read-only transaction cache # Test category memberships. Enable the read-only transaction cache
# temporarily, if not enabled, because this part is strictly read-only, # because this part is strictly read-only, and context.isMemberOf
# and context.isMemberOf is very expensive, when the category list has # is very expensive when the category list has many items.
# many items. with readOnlyTransactionCache():
enabled = getReadOnlyTransactionCache() is not None
try:
if not enabled:
enableReadOnlyTransactionCache()
for c in membership_criterion_category_list: for c in membership_criterion_category_list:
bc = c.split('/', 1)[0] bc = c.split('/', 1)[0]
if (bc not in tested_base_category) and \ if (bc not in tested_base_category) and \
...@@ -176,10 +172,6 @@ class Predicate(XMLObject): ...@@ -176,10 +172,6 @@ class Predicate(XMLObject):
tested_base_category[bc] = tested_base_category[bc] or \ tested_base_category[bc] = tested_base_category[bc] or \
context.isMemberOf(c, context.isMemberOf(c,
strict_membership=strict_membership) strict_membership=strict_membership)
finally:
if not enabled:
disableReadOnlyTransactionCache()
# LOG('predicate test', 0, # LOG('predicate test', 0,
# '%s after single membership to %s' % \ # '%s after single membership to %s' % \
# (tested_base_category[bc], c)) # (tested_base_category[bc], c))
......
...@@ -102,13 +102,8 @@ class TransactionalVariable(dict): ...@@ -102,13 +102,8 @@ class TransactionalVariable(dict):
transactional_variable_pool = local() transactional_variable_pool = local()
_MARKER = object() def getTransactionalVariable():
def getTransactionalVariable(context=_MARKER):
"""Return a transactional variable.""" """Return a transactional variable."""
if context is not _MARKER:
warnings.warn("Passing a parameter to getTransactionalVariable() is"
" deprecated and will not be tolerated in the future",
DeprecationWarning)
try: try:
return transactional_variable_pool.instance return transactional_variable_pool.instance
except AttributeError: except AttributeError:
......
...@@ -39,6 +39,7 @@ import string ...@@ -39,6 +39,7 @@ import string
import pprint import pprint
import re import re
import warnings import warnings
from contextlib import contextmanager
from cStringIO import StringIO from cStringIO import StringIO
from xml.dom.minidom import parse from xml.dom.minidom import parse
from xml.sax.saxutils import escape, quoteattr from xml.sax.saxutils import escape, quoteattr
...@@ -79,17 +80,17 @@ try: ...@@ -79,17 +80,17 @@ try:
except ImportError: except ImportError:
psyco = None psyco = None
@contextmanager
def noReadOnlyTransactionCache():
yield
try: try:
from Products.ERP5Type.Cache import enableReadOnlyTransactionCache, \ from Products.ERP5Type.Cache import \
disableReadOnlyTransactionCache, caching_instance_method readOnlyTransactionCache, caching_instance_method
except ImportError: except ImportError:
LOG('SQLCatalog', WARNING, 'Count not import caching_instance_method, expect slowness.') LOG('SQLCatalog', WARNING, 'Count not import caching_instance_method, expect slowness.')
def doNothing(context):
pass
def caching_instance_method(*args, **kw): def caching_instance_method(*args, **kw):
return lambda method: method return lambda method: method
enableReadOnlyTransactionCache = doNothing readOnlyTransactionCache = noReadOnlyTransactionCache
disableReadOnlyTransactionCache = doNothing
try: try:
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
...@@ -1502,10 +1503,8 @@ class Catalog(Folder, ...@@ -1502,10 +1503,8 @@ class Catalog(Folder,
econtext = getEngine().getContext() econtext = getEngine().getContext()
argument_cache = {} argument_cache = {}
try: with (noReadOnlyTransactionCache if disable_cache else
if not disable_cache: readOnlyTransactionCache)():
enableReadOnlyTransactionCache()
filter_dict = self.filter_dict filter_dict = self.filter_dict
catalogged_object_list_cache = {} catalogged_object_list_cache = {}
for method_name in method_id_list: for method_name in method_id_list:
...@@ -1626,9 +1625,6 @@ class Catalog(Folder, ...@@ -1626,9 +1625,6 @@ class Catalog(Folder,
LOG('SQLCatalog', WARNING, 'could not catalog objects %s with method %s' % (object_list, method_name), LOG('SQLCatalog', WARNING, 'could not catalog objects %s with method %s' % (object_list, method_name),
error=sys.exc_info()) error=sys.exc_info())
raise raise
finally:
if not disable_cache:
disableReadOnlyTransactionCache()
if psyco is not None: if psyco is not None:
psyco.bind(_catalogObjectList) psyco.bind(_catalogObjectList)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment