Merge pull request #648 from odoo-dev/7.0-fix-searchbar-navigation-ged
[odoo/odoo.git] / openerp / modules / registry.py
index 1cfa10e..853fab0 100644 (file)
@@ -50,6 +50,7 @@ class Registry(object):
         self._init = True
         self._init_parent = {}
         self._assertion_report = assertion_report.assertion_report()
+        self.fields_by_model = None
 
         # modules fully loaded (maintained during init phase by `loading` module)
         self._init_modules = set()
@@ -57,16 +58,16 @@ class Registry(object):
         self.db_name = db_name
         self.db = openerp.sql_db.db_connect(db_name)
 
-        # In monoprocess cron jobs flag (pooljobs)
-        self.cron = False
+        # Indicates that the registry is 
+        self.ready = False
 
         # Inter-process signaling (used only when openerp.multi_process is True):
         # The `base_registry_signaling` sequence indicates the whole registry
         # must be reloaded.
         # The `base_cache_signaling sequence` indicates all caches must be
         # invalidated (i.e. cleared).
-        self.base_registry_signaling_sequence = 1
-        self.base_cache_signaling_sequence = 1
+        self.base_registry_signaling_sequence = None
+        self.base_cache_signaling_sequence = None
 
         # Flag indicating if at least one model cache has been cleared.
         # Useful only in a multi-process context.
@@ -109,24 +110,16 @@ class Registry(object):
         and registers them in the registry.
 
         """
-
-        res = []
-
+        models_to_load = [] # need to preserve loading order
         # Instantiate registered classes (via the MetaModel automatic discovery
         # or via explicit constructor call), and add them to the pool.
         for cls in openerp.osv.orm.MetaModel.module_to_models.get(module.name, []):
-            res.append(cls.create_instance(self, cr))
-
-        return res
-
-    def schedule_cron_jobs(self):
-        """ Make the cron thread care about this registry/database jobs.
-        This will initiate the cron thread to check for any pending jobs for
-        this registry/database as soon as possible. Then it will continuously
-        monitor the ir.cron model for future jobs. See openerp.cron for
-        details.
-        """
-        self.cron = True
+            # models register themselves in self.models
+            model = cls.create_instance(self, cr)
+            if model._name not in models_to_load:
+                # avoid double-loading models whose declaration is split
+                models_to_load.append(model._name)
+        return [self.models[m] for m in models_to_load]
 
     def clear_caches(self):
         """ Clear the caches
@@ -152,7 +145,7 @@ class Registry(object):
     @classmethod
     def setup_multi_process_signaling(cls, cr):
         if not openerp.multi_process:
-            return
+            return None, None
 
         # Inter-process signaling:
         # The `base_registry_signaling` sequence indicates the whole registry
@@ -165,6 +158,16 @@ class Registry(object):
             cr.execute("""SELECT nextval('base_registry_signaling')""")
             cr.execute("""CREATE SEQUENCE base_cache_signaling INCREMENT BY 1 START WITH 1""")
             cr.execute("""SELECT nextval('base_cache_signaling')""")
+        
+        cr.execute("""
+                    SELECT base_registry_signaling.last_value,
+                           base_cache_signaling.last_value
+                    FROM base_registry_signaling, base_cache_signaling""")
+        r, c = cr.fetchone()
+        _logger.debug("Multiprocess load registry signaling: [Registry: # %s] "\
+                    "[Cache: # %s]",
+                    r, c)
+        return r, c
 
     @contextmanager
     def cursor(self, auto_commit=True):
@@ -190,18 +193,22 @@ class RegistryManager(object):
     registries_lock = threading.RLock()
 
     @classmethod
-    def get(cls, db_name, force_demo=False, status=None, update_module=False,
-            pooljobs=True):
+    def get(cls, db_name, force_demo=False, status=None, update_module=False):
         """ Return a registry for a given database name."""
-        try:
-            return cls.registries[db_name]
-        except KeyError:
-            return cls.new(db_name, force_demo, status,
-                           update_module, pooljobs)
+        with cls.registries_lock:
+            try:
+                return cls.registries[db_name]
+            except KeyError:
+                return cls.new(db_name, force_demo, status,
+                               update_module)
+            finally:
+                # set db tracker - cleaned up at the WSGI
+                # dispatching phase in openerp.service.wsgi_server.application
+                threading.current_thread().dbname = db_name
 
     @classmethod
     def new(cls, db_name, force_demo=False, status=None,
-            update_module=False, pooljobs=True):
+            update_module=False):
         """ Create and return a new registry for a given database name.
 
         The (possibly) previous registry for that database name is discarded.
@@ -218,37 +225,39 @@ class RegistryManager(object):
             cls.delete(db_name)
             cls.registries[db_name] = registry
             try:
+                with registry.cursor() as cr:
+                    seq_registry, seq_cache = Registry.setup_multi_process_signaling(cr)
+                    registry.base_registry_signaling_sequence = seq_registry
+                    registry.base_cache_signaling_sequence = seq_cache
                 # This should be a method on Registry
                 openerp.modules.load_modules(registry.db, force_demo, status, update_module)
             except Exception:
                 del cls.registries[db_name]
                 raise
 
+            # load_modules() above can replace the registry by calling
+            # indirectly new() again (when modules have to be uninstalled).
+            # Yeah, crazy.
+            registry = cls.registries[db_name]
+
             cr = registry.db.cursor()
             try:
-                Registry.setup_multi_process_signaling(cr)
                 registry.do_parent_store(cr)
                 registry.get('ir.actions.report.xml').register_all(cr)
                 cr.commit()
             finally:
                 cr.close()
 
-        if pooljobs:
-            registry.schedule_cron_jobs()
+        registry.ready = True
 
+        if update_module:
+            # only in case of update, otherwise we'll have an infinite reload loop!
+            cls.signal_registry_change(db_name)
         return registry
 
     @classmethod
     def delete(cls, db_name):
-        """Delete the registry linked to a given database.
-
-        This also cleans the associated caches. For good measure this also
-        cancels the associated cron job. But please note that the cron job can
-        be running and take some time before ending, and that you should not
-        remove a registry if it can still be used by some thread. So it might
-        be necessary to call yourself openerp.cron.Agent.cancel(db_name) and
-        and join (i.e. wait for) the thread.
-        """
+        """Delete the registry linked to a given database.  """
         with cls.registries_lock:
             if db_name in cls.registries:
                 cls.registries[db_name].clear_caches()
@@ -279,7 +288,7 @@ class RegistryManager(object):
     @classmethod
     def check_registry_signaling(cls, db_name):
         if openerp.multi_process and db_name in cls.registries:
-            registry = cls.get(db_name, pooljobs=False)
+            registry = cls.get(db_name)
             cr = registry.db.cursor()
             try:
                 cr.execute("""
@@ -287,19 +296,20 @@ class RegistryManager(object):
                            base_cache_signaling.last_value
                     FROM base_registry_signaling, base_cache_signaling""")
                 r, c = cr.fetchone()
+                _logger.debug("Multiprocess signaling check: [Registry - old# %s new# %s] "\
+                    "[Cache - old# %s new# %s]",
+                    registry.base_registry_signaling_sequence, r,
+                    registry.base_cache_signaling_sequence, c)
                 # Check if the model registry must be reloaded (e.g. after the
                 # database has been updated by another process).
-                if registry.base_registry_signaling_sequence != r:
+                if registry.base_registry_signaling_sequence is not None and registry.base_registry_signaling_sequence != r:
                     _logger.info("Reloading the model registry after database signaling.")
-                    # Don't run the cron in the Gunicorn worker.
-                    registry = cls.new(db_name, pooljobs=False)
-                    registry.base_registry_signaling_sequence = r
+                    registry = cls.new(db_name)
                 # Check if the model caches must be invalidated (e.g. after a write
                 # occured on another process). Don't clear right after a registry
                 # has been reload.
-                elif registry.base_cache_signaling_sequence != c:
+                elif registry.base_cache_signaling_sequence is not None and registry.base_cache_signaling_sequence != c:
                     _logger.info("Invalidating all model caches after database signaling.")
-                    registry.base_cache_signaling_sequence = c
                     registry.clear_caches()
                     registry.reset_any_cache_cleared()
                     # One possible reason caches have been invalidated is the
@@ -309,6 +319,8 @@ class RegistryManager(object):
                         for column in model._columns.values():
                             if hasattr(column, 'digits_change'):
                                 column.digits_change(cr)
+                registry.base_registry_signaling_sequence = r
+                registry.base_cache_signaling_sequence = c
             finally:
                 cr.close()
 
@@ -317,7 +329,7 @@ class RegistryManager(object):
         if openerp.multi_process and db_name in cls.registries:
             # Check the registries if any cache has been cleared and signal it
             # through the database to other processes.
-            registry = cls.get(db_name, pooljobs=False)
+            registry = cls.get(db_name)
             if registry.any_cache_cleared():
                 _logger.info("At least one model cache has been cleared, signaling through the database.")
                 cr = registry.db.cursor()
@@ -333,7 +345,8 @@ class RegistryManager(object):
     @classmethod
     def signal_registry_change(cls, db_name):
         if openerp.multi_process and db_name in cls.registries:
-            registry = cls.get(db_name, pooljobs=False)
+            _logger.info("Registry changed, signaling through the database")
+            registry = cls.get(db_name)
             cr = registry.db.cursor()
             r = 1
             try: