From ba70c43dc19a13a950454630a744c8b0e20b9e5c Mon Sep 17 00:00:00 2001
From: Vo Minh Thu <vmt@openerp.com>
Date: Thu, 2 Feb 2012 18:35:22 +0100
Subject: [PATCH] [IMP] inter-process signaling, proof-of-concept.

bzr revid: vmt@openerp.com-20120202173522-2grq11zfm7855i6s
---
 gunicorn.conf.py             |  6 +++--
 openerp-server               |  2 +-
 openerp/addons/base/base.sql |  8 +++++++
 openerp/osv/osv.py           | 44 ++++++++++++++++++++++++++++++++++++
 openerp/service/__init__.py  |  2 +-
 openerp/wsgi.py              |  8 +++----
 6 files changed, 62 insertions(+), 8 deletions(-)

diff --git a/gunicorn.conf.py b/gunicorn.conf.py
index 7f23553a1ded..8e94fc7cea61 100644
--- a/gunicorn.conf.py
+++ b/gunicorn.conf.py
@@ -17,7 +17,7 @@ pidfile = '.gunicorn.pid'
 # Gunicorn recommends 2-4 x number_of_cpu_cores, but
 # you'll want to vary this a bit to find the best for your
 # particular work load.
-workers = 4
+workers = 1
 
 # Some application-wide initialization is needed.
 on_starting = openerp.wsgi.on_starting
@@ -31,6 +31,8 @@ timeout = 240
 
 max_requests = 2000
 
+#accesslog = '/tmp/blah.txt'
+
 # Equivalent of --load command-line option
 openerp.conf.server_wide_modules = ['web']
 
@@ -39,7 +41,7 @@ conf = openerp.tools.config
 
 # Path to the OpenERP Addons repository (comma-separated for
 # multiple locations)
-conf['addons_path'] = '/home/openerp/addons/trunk,/home/openerp/web/trunk/addons'
+conf['addons_path'] = '/home/thu/repos/addons/trunk,/home/thu/repos/web/trunk/addons'
 
 # Optional database config if not using local socket
 #conf['db_name'] = 'mycompany'
diff --git a/openerp-server b/openerp-server
index ab31c3803cd9..4111b54ed99e 100755
--- a/openerp-server
+++ b/openerp-server
@@ -247,7 +247,7 @@ if __name__ == "__main__":
             # Call any post_load hook.
             info = openerp.modules.module.load_information_from_description_file(m)
             if info['post_load']:
-                getattr(sys.modules[m], info['post_load'])()
+                getattr(sys.modules['openerp.addons.' + m], info['post_load'])()
         except Exception:
             msg = ''
             if m == 'web':
diff --git a/openerp/addons/base/base.sql b/openerp/addons/base/base.sql
index 409ce81b2856..3f7f2e3581c0 100644
--- a/openerp/addons/base/base.sql
+++ b/openerp/addons/base/base.sql
@@ -347,6 +347,14 @@ CREATE TABLE ir_model_data (
     res_id integer, primary key(id)
 );
 
+-- Inter-process signaling:
+-- 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).
+CREATE SEQUENCE base_registry_signaling INCREMENT BY 1 START WITH 1;
+CREATE SEQUENCE base_cache_signaling INCREMENT BY 1 START WITH 1;
+
 ---------------------------------
 -- Users
 ---------------------------------
diff --git a/openerp/osv/osv.py b/openerp/osv/osv.py
index 44597bf65076..d46f8a1bb4fc 100644
--- a/openerp/osv/osv.py
+++ b/openerp/osv/osv.py
@@ -34,6 +34,8 @@ from openerp.tools.translate import translate
 from openerp.osv.orm import MetaModel, Model, TransientModel, AbstractModel
 import openerp.exceptions
 
+_logger = logging.getLogger(__name__)
+
 # Deprecated.
 class except_osv(Exception):
     def __init__(self, name, value):
@@ -43,6 +45,14 @@ class except_osv(Exception):
 
 service = None
 
+# Inter-process signaling:
+# 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).
+base_registry_signaling_sequence = None
+base_cache_signaling_sequence = None
+
 class object_proxy(object):
     def __init__(self):
         self.logger = logging.getLogger('web-services')
@@ -167,6 +177,40 @@ class object_proxy(object):
 
     @check
     def execute(self, db, uid, obj, method, *args, **kw):
+
+        # Check if the model registry must be reloaded (e.g. after the
+        # database has been updated by another process).
+        cr = pooler.get_db(db).cursor()
+        registry_reloaded = False
+        try:
+            cr.execute('select last_value from base_registry_signaling')
+            r = cr.fetchone()[0]
+            global base_registry_signaling_sequence
+            if base_registry_signaling_sequence != r:
+                _logger.info("Reloading the model registry after database signaling.")
+                base_registry_signaling_sequence = r
+                # Don't run the cron in the Gunicorn worker.
+                openerp.modules.registry.RegistryManager.new(db, pooljobs=False)
+                registry_reloaded = True
+        finally:
+            cr.close()
+
+        # 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.
+        cr = pooler.get_db(db).cursor()
+        try:
+            cr.execute('select last_value from base_cache_signaling')
+            r = cr.fetchone()[0]
+            global base_cache_signaling_sequence
+            if base_cache_signaling_sequence != r and not registry_reloaded:
+                _logger.info("Invalidating all model caches after database signaling.")
+                base_cache_signaling_sequence = r
+                registry = openerp.modules.registry.RegistryManager.get(db, pooljobs=False)
+                registry.clear_caches()
+        finally:
+            cr.close()
+
         cr = pooler.get_db(db).cursor()
         try:
             try:
diff --git a/openerp/service/__init__.py b/openerp/service/__init__.py
index 1bb83dfd2281..508ad136a5e7 100644
--- a/openerp/service/__init__.py
+++ b/openerp/service/__init__.py
@@ -63,7 +63,7 @@ def start_services():
     netrpc_server.init_servers()
 
     # Start the main cron thread.
-    openerp.cron.start_master_thread()
+    #openerp.cron.start_master_thread()
 
     # Start the top-level servers threads (normally HTTP, HTTPS, and NETRPC).
     openerp.netsvc.Server.startAll()
diff --git a/openerp/wsgi.py b/openerp/wsgi.py
index 2b7216481967..90e6072eccd9 100644
--- a/openerp/wsgi.py
+++ b/openerp/wsgi.py
@@ -421,7 +421,7 @@ def serve():
     port = config['xmlrpc_port']
     try:
         import werkzeug.serving
-        httpd = werkzeug.serving.make_server(interface, port, application, threaded=True)
+        httpd = werkzeug.serving.make_server(interface, port, application, threaded=False)
         logging.getLogger('wsgi').info('HTTP service (werkzeug) running on %s:%s', interface, port)
     except ImportError:
         import wsgiref.simple_server
@@ -436,7 +436,7 @@ def start_server():
 
     The WSGI server can be shutdown with stop_server() below.
     """
-    threading.Thread(target=openerp.wsgi.serve).start()
+    threading.Thread(name='WSGI server', target=openerp.wsgi.serve).start()
 
 def stop_server():
     """ Initiate the shutdown of the WSGI server.
@@ -462,11 +462,11 @@ def on_starting(server):
     openerp.modules.loading.open_openerp_namespace()
     for m in openerp.conf.server_wide_modules:
         try:
-            __import__(m)
+            __import__('openerp.addons.' + m)
             # Call any post_load hook.
             info = openerp.modules.module.load_information_from_description_file(m)
             if info['post_load']:
-                getattr(sys.modules[m], info['post_load'])()
+                getattr(sys.modules['openerp.addons.' + m], info['post_load'])()
         except Exception:
             msg = ''
             if m == 'web':
-- 
GitLab