Add cache management on RPC
issue8233 review257231002
This commit is contained in:
@@ -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
@@ -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.
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = {}
|
||||
|
||||
@@ -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
@@ -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
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user