Add cache management on RPC

issue8233
review257231002
This commit is contained in:
Cédric Krier
2019-04-08 10:10:40 +02:00
parent d49a38e313
commit 0fbf5cecd2
9 changed files with 77 additions and 30 deletions
+1
View File
@@ -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__
+23 -1
View File
@@ -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 @@ Instance attributes are:
.. 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.
+3 -1
View File
@@ -139,7 +139,9 @@ class ActionKeyword(ModelSQL, ModelView):
@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):
+1 -1
View File
@@ -312,7 +312,7 @@ class ViewTreeState(ModelSQL, ModelView):
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
+2 -2
View File
@@ -35,8 +35,8 @@ class Model(URLMixin, PoolBase, metaclass=ModelMeta):
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),
}
+2 -2
View File
@@ -118,8 +118,8 @@ class ModelView(Model):
@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 = {}
+4 -1
View File
@@ -214,4 +214,7 @@ def _dispatch(request, pool, *args, **kwargs):
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
+19 -2
View File
@@ -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 @@ class RPC(object):
'''
__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 @@ class RPC(object):
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 @@ class RPC(object):
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()),
}
+22 -20
View File
@@ -116,6 +116,27 @@ class TrytondWSGI(object):
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 @@ class TrytondWSGI(object):
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 @@ class TrytondWSGI(object):
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):