From 347b899bd79dd8c33b47f52444dcb9d9c554c2cb Mon Sep 17 00:00:00 2001
From: Arnaud Fontaine <arnaud.fontaine@nexedi.com>
Date: Tue, 7 Feb 2012 18:00:42 +0900
Subject: [PATCH] Avoid costly checking if Component modules are synchronized
 on __getattribute__.

---
 product/ERP5Type/Tool/ComponentTool.py      | 60 +++++++++++++--------
 product/ERP5Type/dynamic/component_class.py | 27 ----------
 product/ERP5Type/dynamic/dynamic_module.py  |  4 +-
 3 files changed, 40 insertions(+), 51 deletions(-)

diff --git a/product/ERP5Type/Tool/ComponentTool.py b/product/ERP5Type/Tool/ComponentTool.py
index f2cbbe2793..364a73bfe2 100644
--- a/product/ERP5Type/Tool/ComponentTool.py
+++ b/product/ERP5Type/Tool/ComponentTool.py
@@ -30,12 +30,14 @@
 import transaction
 
 from AccessControl import ClassSecurityInfo
-from Products.ERP5Type.Tool.BaseTool import BaseTool
 from Products.ERP5Type import Permissions
+from Products.ERP5Type.Tool.BaseTool import BaseTool
+from Products.ERP5Type.Base import Base
 from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
 
 from zLOG import LOG, INFO, WARNING
 
+_last_sync = -1
 class ComponentTool(BaseTool):
   """
     This tool provides methods to load the the different types 
@@ -50,38 +52,52 @@ class ComponentTool(BaseTool):
   security.declareObjectProtected(Permissions.AccessContentsInformation)
 
   security.declareProtected(Permissions.ModifyPortalContent, 'reset')
-  def reset(self, is_sync=False):
+  def reset(self, force=True):
     """
     XXX-arnau: global reset
     """
-    LOG("ERP5Type.Tool.ComponentTool", INFO, "Resetting Components")
-
-    import erp5.component
-
     portal = self.getPortalObject()
 
-    if not is_sync:
+    # XXX-arnau: copy/paste from portal_type_class, but is this really
+    # necessary as even for Portal Type classes, synchronizeDynamicModules
+    # seems to always called with force=True?
+    global last_sync
+    if force:
+      # hard invalidation to force sync between nodes
       portal.newCacheCookie('component_classes')
-      erp5.component._last_reset = portal.getCacheCookie('component_classes')
+      last_sync = portal.getCacheCookie('component_classes')
+    else:
+      cookie = portal.getCacheCookie('component_classes')
+      if cookie == last_sync:
+        type_tool.resetDynamicDocumentsOnceAtTransactionBoundary()
+        return
+      last_sync = cookie
+
+    LOG("ERP5Type.Tool.ComponentTool", INFO, "Resetting Components")
 
     type_tool = portal.portal_types
-    container_type_info = type_tool.getTypeInfo(self.getPortalType())
 
-    for content_type in container_type_info.getTypeAllowedContentTypeList():
-      module_name = content_type.split(' ')[0].lower()
+    allowed_content_type_list = type_tool.getTypeInfo(
+      self.getPortalType()).getTypeAllowedContentTypeList()
 
-      try:
-        module = getattr(erp5.component, module_name)
-      # XXX-arnau: not everything is defined yet...
-      except AttributeError:
-        pass
-      else:
-        for name in module.__dict__.keys():
-          if name[0] != '_':
-            LOG("ERP5Type.Tool.ComponentTool", INFO,
-                "Resetting erp5.component.%s.%s" % (module_name, name))
+    import erp5.component
 
-            delattr(module, name)
+    with Base.aq_method_lock:
+      for content_type in allowed_content_type_list:
+        module_name = content_type.split(' ')[0].lower()
+
+        try:
+          module = getattr(erp5.component, module_name)
+        # XXX-arnau: not everything is defined yet...
+        except AttributeError:
+          pass
+        else:
+          for name in module.__dict__.keys():
+            if name[0] != '_':
+              LOG("ERP5Type.Tool.ComponentTool", INFO,
+                  "Resetting erp5.component.%s.%s" % (module_name, name))
+
+              delattr(module, name)
 
     type_tool.resetDynamicDocumentsOnceAtTransactionBoundary()
 
diff --git a/product/ERP5Type/dynamic/component_class.py b/product/ERP5Type/dynamic/component_class.py
index 5b2261c0c0..dc74ec8bca 100644
--- a/product/ERP5Type/dynamic/component_class.py
+++ b/product/ERP5Type/dynamic/component_class.py
@@ -28,33 +28,6 @@
 
 from Products.ERP5.ERP5Site import getSite
 from types import ModuleType
-
-class ComponentModule(ModuleType):
-  _resetting = False
-  _last_reset = -1
-
-  def __getattribute__(self, name):
-    """
-    Synchronize between ZEO clients
-
-    XXX-arnau: surely bad from a performance POV and not thread-safe
-    """
-    if name[0] == '_' or self._resetting:
-      return super(ComponentModule, self).__getattribute__(name)
-
-    import erp5.component
-    site = getSite()
-    cookie = site.getCacheCookie('component_classes')
-    if self._last_reset == -1:
-      self._last_reset = site.getCacheCookie('component_classes')
-    elif cookie != self._last_reset:
-      self._resetting = True
-      site.portal_components.reset(is_sync=True)
-      self._resetting = False
-
-    return super(ComponentModule, self).__getattribute__(name)
-
-from types import ModuleType
 from zLOG import LOG, INFO
 
 def generateComponentClassWrapper(namespace, portal_type):
diff --git a/product/ERP5Type/dynamic/dynamic_module.py b/product/ERP5Type/dynamic/dynamic_module.py
index efd8a64263..d000784e5c 100644
--- a/product/ERP5Type/dynamic/dynamic_module.py
+++ b/product/ERP5Type/dynamic/dynamic_module.py
@@ -122,9 +122,9 @@ def initializeDynamicModules():
                                                 loadTempPortalTypeClass)
 
   # Components
-  from component_class import ComponentModule, generateComponentClassWrapper
+  from component_class import generateComponentClassWrapper
 
-  erp5.component = ComponentModule("erp5.component")
+  erp5.component = ModuleType("erp5.component")
   sys.modules["erp5.component"] = erp5.component
 
   erp5.component.extension = registerDynamicModule(
-- 
2.30.9