changeset 34ec4c3b700c in trytond:default
details: https://hg.tryton.org/trytond?cmd=changeset;node=34ec4c3b700c
description:
        Add cache management on RPC

        issue8233
        review257231002
diffstat:

 CHANGELOG                       |   1 +
 doc/ref/rpc.rst                 |  24 ++++++++++++++++++++++-
 trytond/ir/action.py            |   4 ++-
 trytond/ir/ui/view.py           |   2 +-
 trytond/model/model.py          |   4 +-
 trytond/model/modelview.py      |   4 +-
 trytond/protocols/dispatcher.py |   5 +++-
 trytond/rpc.py                  |  21 ++++++++++++++++++-
 trytond/wsgi.py                 |  42 +++++++++++++++++++++-------------------
 9 files changed, 77 insertions(+), 30 deletions(-)

diffs (229 lines):

diff -r 691d0ad1b947 -r 34ec4c3b700c CHANGELOG
--- a/CHANGELOG Mon Apr 08 10:02:54 2019 +0200
+++ b/CHANGELOG Mon Apr 08 10:10:40 2019 +0200
@@ -1,3 +1,4 @@
+* Add cache on RPC
 * Remove support for Python 3.4
 * Allow to update record when importing data
 * Set context on record instantiated by Field.__set__
diff -r 691d0ad1b947 -r 34ec4c3b700c doc/ref/rpc.rst
--- a/doc/ref/rpc.rst   Mon Apr 08 10:02:54 2019 +0200
+++ b/doc/ref/rpc.rst   Mon Apr 08 10:10:40 2019 +0200
@@ -5,7 +5,7 @@
 RPC
 ===
 
-.. class:: RPC([readonly[, instantiate[, result[, check_access[, unique[, 
fresh_session]]]]]])
+.. class:: RPC([readonly[, instantiate[, result[, check_access[, unique[, 
fresh_session[, cache]]]]]]])
 
 RPC is an object to define the behavior of Remote Procedure Call.
 
@@ -35,3 +35,25 @@
 .. attribute:: RPC.fresh_session
 
     If set, it requires a fresh session. Default is `False`.
+
+.. attribute:: RPC.cache
+
+    A :class:`RPCCache` instance to compute the cache duration for the answer.
+
+========
+RPCCache
+========
+
+.. class:: RPCCache([days[, seconds])
+
+Instance attributes are:
+
+.. attribute:: RPC.duration
+
+    A timdelta instance.
+
+Instance methods are:
+
+.. method:: RCP.headers
+
+    Returns a dictionary of the headers.
diff -r 691d0ad1b947 -r 34ec4c3b700c trytond/ir/action.py
--- a/trytond/ir/action.py      Mon Apr 08 10:02:54 2019 +0200
+++ b/trytond/ir/action.py      Mon Apr 08 10:10:40 2019 +0200
@@ -139,7 +139,9 @@
     @classmethod
     def __setup__(cls):
         super(ActionKeyword, cls).__setup__()
-        cls.__rpc__.update({'get_keyword': RPC()})
+        cls.__rpc__.update({
+                'get_keyword': RPC(cache=dict(days=1)),
+                })
 
     @classmethod
     def __register__(cls, module_name):
diff -r 691d0ad1b947 -r 34ec4c3b700c trytond/ir/ui/view.py
--- a/trytond/ir/ui/view.py     Mon Apr 08 10:02:54 2019 +0200
+++ b/trytond/ir/ui/view.py     Mon Apr 08 10:10:40 2019 +0200
@@ -312,7 +312,7 @@
         super(ViewTreeState, cls).__setup__()
         cls.__rpc__.update({
                 'set': RPC(readonly=False, check_access=False),
-                'get': RPC(check_access=False),
+                'get': RPC(check_access=False, cache=dict(days=1)),
                 })
 
     @classmethod
diff -r 691d0ad1b947 -r 34ec4c3b700c trytond/model/model.py
--- a/trytond/model/model.py    Mon Apr 08 10:02:54 2019 +0200
+++ b/trytond/model/model.py    Mon Apr 08 10:10:40 2019 +0200
@@ -35,8 +35,8 @@
     def __setup__(cls):
         super(Model, cls).__setup__()
         cls.__rpc__ = {
-            'default_get': RPC(),
-            'fields_get': RPC(),
+            'default_get': RPC(cache=dict(seconds=5 * 60)),
+            'fields_get': RPC(cache=dict(days=1)),
             'pre_validate': RPC(instantiate=0),
             }
 
diff -r 691d0ad1b947 -r 34ec4c3b700c trytond/model/modelview.py
--- a/trytond/model/modelview.py        Mon Apr 08 10:02:54 2019 +0200
+++ b/trytond/model/modelview.py        Mon Apr 08 10:10:40 2019 +0200
@@ -118,8 +118,8 @@
     @classmethod
     def __setup__(cls):
         super(ModelView, cls).__setup__()
-        cls.__rpc__['fields_view_get'] = RPC()
-        cls.__rpc__['view_toolbar_get'] = RPC()
+        cls.__rpc__['fields_view_get'] = RPC(cache=dict(days=1))
+        cls.__rpc__['view_toolbar_get'] = RPC(cache=dict(days=1))
         cls.__rpc__['on_change'] = RPC(instantiate=0)
         cls.__rpc__['on_change_with'] = RPC(instantiate=0)
         cls._buttons = {}
diff -r 691d0ad1b947 -r 34ec4c3b700c trytond/protocols/dispatcher.py
--- a/trytond/protocols/dispatcher.py   Mon Apr 08 10:02:54 2019 +0200
+++ b/trytond/protocols/dispatcher.py   Mon Apr 08 10:10:40 2019 +0200
@@ -214,4 +214,7 @@
             context = {'_request': request.context}
             security.reset(pool.database_name, session, context=context)
         logger.debug('Result: %s', result)
-        return result
+        response = app.make_response(request, result)
+        if rpc.readonly and rpc.cache:
+            response.headers.extend(rpc.cache.headers())
+        return response
diff -r 691d0ad1b947 -r 34ec4c3b700c trytond/rpc.py
--- a/trytond/rpc.py    Mon Apr 08 10:02:54 2019 +0200
+++ b/trytond/rpc.py    Mon Apr 08 10:10:40 2019 +0200
@@ -1,6 +1,7 @@
 # This file is part of Tryton.  The COPYRIGHT file at the top level of
 # this repository contains the full copyright notices and license terms.
 import copy
+import datetime as dt
 
 from trytond.transaction import Transaction
 
@@ -19,10 +20,10 @@
     '''
 
     __slots__ = ('readonly', 'instantiate', 'result', 'check_access',
-        'fresh_session', 'unique')
+        'fresh_session', 'unique', 'cache')
 
     def __init__(self, readonly=True, instantiate=None, result=None,
-            check_access=True, fresh_session=False, unique=True):
+            check_access=True, fresh_session=False, unique=True, cache=None):
         self.readonly = readonly
         self.instantiate = instantiate
         if result is None:
@@ -31,6 +32,10 @@
         self.check_access = check_access
         self.fresh_session = fresh_session
         self.unique = unique
+        if cache:
+            if not isinstance(cache, RPCCache):
+                cache = RPCCache(**cache)
+        self.cache = cache
 
     def convert(self, obj, *args, **kwargs):
         args = list(args)
@@ -70,3 +75,15 @@
         if self.check_access:
             context['_check_access'] = True
         return args, kwargs, context, timestamp
+
+
+class RPCCache:
+    __slots__ = ('duration',)
+
+    def __init__(self, days=0, seconds=0):
+        self.duration = dt.timedelta(days=days, seconds=seconds)
+
+    def headers(self):
+        return {
+            'X-Tryton-Cache': int(self.duration.total_seconds()),
+            }
diff -r 691d0ad1b947 -r 34ec4c3b700c trytond/wsgi.py
--- a/trytond/wsgi.py   Mon Apr 08 10:02:54 2019 +0200
+++ b/trytond/wsgi.py   Mon Apr 08 10:10:40 2019 +0200
@@ -116,6 +116,27 @@
                     response = rv
             return response
 
+    def make_response(self, request, data):
+        for cls in self.protocols:
+            for mimetype, _ in request.accept_mimetypes:
+                if cls.content_type in mimetype:
+                    response = cls.response(data, request)
+                    break
+            else:
+                continue
+            break
+        else:
+            for cls in self.protocols:
+                if cls.content_type in request.environ.get('CONTENT_TYPE', ''):
+                    response = cls.response(data, request)
+                    break
+            else:
+                if isinstance(data, Exception):
+                    response = InternalServerError(data)
+                else:
+                    response = Response(data)
+        return response
+
     def wsgi_app(self, environ, start_response):
         for cls in self.protocols:
             if cls.content_type in environ.get('CONTENT_TYPE', ''):
@@ -135,24 +156,7 @@
 
         data = self.dispatch_request(request)
         if not isinstance(data, (Response, HTTPException)):
-            for cls in self.protocols:
-                for mimetype, _ in request.accept_mimetypes:
-                    if cls.content_type in mimetype:
-                        response = cls.response(data, request)
-                        break
-                else:
-                    continue
-                break
-            else:
-                for cls in self.protocols:
-                    if cls.content_type in environ.get('CONTENT_TYPE', ''):
-                        response = cls.response(data, request)
-                        break
-                else:
-                    if isinstance(data, Exception):
-                        response = InternalServerError(data)
-                    else:
-                        response = Response(data)
+            response = self.make_response(request, data)
         else:
             response = data
 
@@ -167,8 +171,6 @@
                 response.headers['Access-Control-Allow-Headers'] = headers
             response.headers['Access-Control-Max-Age'] = config.getint(
                 'web', 'cache_timeout')
-
-        # TODO custom process response
         return response(environ, start_response)
 
     def __call__(self, environ, start_response):

Reply via email to