-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
233 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
.. _cache: | ||
|
||
Cache | ||
===== | ||
|
||
Description | ||
----------- | ||
|
||
Usually django cache is used to cache page or basic ``GET`` request. However, in the context of gRPC, we use ``POST`` request and there is no `native cache system in gRPC in Python <https://github.com/grpc/grpc/issues/7945>`_. | ||
|
||
Fortunately, DSG bring a layer of abstraction between gRPC request and Django request allowing us to use Django cache system. | ||
|
||
To enable it follow the `Django instructions <https://docs.djangoproject.com/fr/5.0/topics/cache/#setting-up-the-cache>`_ then use the :ref:`cache_endpoint <cache-endpoint>` decorator or the :ref:`cache_endpoint_with_deleter <cache-endpoint-with-deleter>` to cache your endpoint. | ||
|
||
.. _cache-endpoint: | ||
|
||
cache_endpoint | ||
-------------- | ||
|
||
Th :func:`cache_endpoint <django_socio_grpc.decorators.cache_endpoint>` decorator is used to adapt the `cache_page <https://docs.djangoproject.com/fr/5.0/topics/cache/#django.views.decorators.cache.cache_page>`_ decorator to work with grpc. | ||
|
||
It took the same parameters and do the exact same things. See :ref:`Use Django decorators in DSG <use-django-decorators-in-dsg>` for more informations. | ||
|
||
This decorator will cache response depending on: | ||
|
||
* :ref:`Filters <filters>` | ||
* :ref:`Pagination <pagination>` | ||
|
||
Meaning that if you have a filter in your request, the cache will be different for each filter. | ||
|
||
.. warning:: | ||
|
||
If you have request parameters that are not considered as filters or pagination, the cache will not be different for each request. | ||
|
||
|
||
Example: | ||
|
||
.. code-block:: python | ||
from django_socio_grpc.decorators import cache_endpoint | ||
... | ||
class UnitTestModelWithCacheService(generics.AsyncModelService, mixins.AsyncStreamModelMixin): | ||
queryset = UnitTestModel.objects.all().order_by("id") | ||
serializer_class = UnitTestModelWithCacheSerializer | ||
@grpc_action( | ||
request=[], | ||
response=UnitTestModelWithCacheSerializer, | ||
use_generation_plugins=[ | ||
ListGenerationPlugin(response=True), | ||
], | ||
) | ||
@cache_endpoint(300) | ||
async def List(self, request, context): | ||
return await super().List(request, context) | ||
.. _cache-endpoint-with-deleter: | ||
|
||
cache_endpoint_with_deleter | ||
--------------------------- | ||
|
||
The :func:`cache_endpoint_with_deleter <django_socio_grpc.decorators.cache_endpoint_with_deleter>` decorator work the same :ref:`cache_endpoint <cache-endpoint>` but allow to automatically delete the cache when a django signals is called from the models passed in parameters. | ||
|
||
As DSG is an API framework it's logic to add utils to invalidate cache if data is created, updated or deleted. | ||
|
||
.. warning:: | ||
|
||
The cache will not be deleted if using bulk operations. This also integrate the usage of filter(...).update() method. | ||
See `caveats of each meathod you wish to use to be sure of the behavior <https://docs.djangoproject.com/en/5.0/ref/models/querysets/#bulk-create>`_ | ||
|
||
There is also caveats to understand when usings cache-endpoint-with-deleter. As only Redis cache allow a pattern like deleter, if not using redis cache each specified signals on the specified models of the deleter will delete the entire cache. | ||
|
||
To address this issue, you can: | ||
|
||
* Use a `redis cache <https://docs.djangoproject.com/fr/5.0/topics/cache/#redis>`_ | ||
* Use a cache per model | ||
|
||
.. note:: | ||
|
||
If you do not follow above advice a warning will show up everytimes you start the server. To disable it use the :ref:`ENABLE_CACHE_WARNING_ON_DELETER <settings-cache-warning-on-deleter>` setting. | ||
|
||
|
||
Example: | ||
|
||
.. code-block:: python | ||
# SETTINGS | ||
CACHES = { | ||
"UnitTestModelCache": { | ||
"BACKEND": "django.core.cache.backends.db.DatabaseCache", | ||
"LOCATION": "unit_test_model_cache_table", | ||
} | ||
} | ||
# SERVICES | ||
from django_socio_grpc.decorators import cache_endpoint_with_deleter | ||
... | ||
class UnitTestModelWithCacheService(generics.AsyncModelService, mixins.AsyncStreamModelMixin): | ||
queryset = UnitTestModel.objects.all().order_by("id") | ||
serializer_class = UnitTestModelWithCacheSerializer | ||
@grpc_action( | ||
request=[], | ||
response=UnitTestModelWithCacheSerializer, | ||
use_generation_plugins=[ | ||
ListGenerationPlugin(response=True), | ||
], | ||
) | ||
@cache_endpoint_with_deleter( | ||
300, | ||
key_prefix="UnitTestModel", | ||
cache="UnitTestModelCache", | ||
senders=(UnitTestModel,), | ||
) | ||
async def List(self, request, context): | ||
return await super().List(request, context) | ||
.. _vary-on-metadata: | ||
|
||
vary_on_metadata | ||
---------------- | ||
|
||
Working like django `vary_on_headers <https://docs.djangoproject.com/fr/5.0/topics/cache/#using-vary-headers>`_ it's just a convenient renaming using :ref:`Use Django decorators in DSG <use-django-decorators-in-dsg>`. | ||
|
||
It allow the cache to also `vary on metadata <https://github.com/grpc/grpc/tree/master/examples/python/metadata>`_ and not only filters and paginations. | ||
|
||
Example: | ||
|
||
|
||
.. code-block:: python | ||
from django_socio_grpc.decorators import cache_endpoint, vary_on_metadata | ||
... | ||
class UnitTestModelWithCacheService(generics.AsyncModelService, mixins.AsyncStreamModelMixin): | ||
queryset = UnitTestModel.objects.all().order_by("id") | ||
serializer_class = UnitTestModelWithCacheSerializer | ||
@grpc_action( | ||
request=[], | ||
response=UnitTestModelWithCacheSerializer, | ||
use_generation_plugins=[ | ||
ListGenerationPlugin(response=True), | ||
], | ||
) | ||
@cache_endpoint(300) | ||
@vary_on_metadata("custom-metadata", "another-metadata") | ||
async def List(self, request, context): | ||
return await super().List(request, context) | ||
.. _any-other-decorator: | ||
|
||
Any other decorator | ||
------------------- | ||
|
||
As you can use :ref:`Django decorators in DSG <use-django-decorators-in-dsg>`. You can try to use any django decorators as long as they are wrapped into :func:`http_to_grpc decorator <django_socio_grpc.decorators.http_to_grpc>`. | ||
|
||
If the one you are trying to use is not working as expected and it's not listed in the documentation page please fill an issue. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,4 +18,5 @@ Features | |
logging | ||
streaming | ||
commands | ||
cache | ||
health-check |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
.. _use-django-decorators-in-dsg: | ||
|
||
Use Django decorators in DSG | ||
============================= | ||
|
||
In Django, decorators expect to decorate a function that takes a `django.http.HttpRequest <https://docs.djangoproject.com/en/5.0/ref/request-response/#httprequest-objects>`_ as its first argument and return a `django.http.HttpResponse <https://docs.djangoproject.com/en/5.0/ref/request-response/#httpresponse-objects>`_. | ||
|
||
In DSG, decorators expect to decorate a class method that take 2 arguments: a `grpc Message<https://grpc.io/docs/languages/python/quickstart/#update-the-server>`_ as request and the `grpc context <https://grpc.github.io/grpc/python/grpc_asyncio.html#server-side-context>`_, and return a `grpc Message <https://grpc.io/docs/languages/python/quickstart/#update-the-server>`_ as response. | ||
|
||
Both are based on HTTP protocol. So it's possible to find similar concept and usage. | ||
|
||
By using :ref:`DSG proxy request and proxy response <request-and-response-proxy>` it is possible to simulate django behavior and apply it to gRPC calls. | ||
|
||
See :func:`http_to_grpc decorator <django_socio_grpc.decorators.http_to_grpc>` for more detailsa and parameters. | ||
|
||
.. _simple-example: | ||
|
||
Simple example | ||
-------------- | ||
|
||
|
||
.. code-block:: python | ||
from django_socio_grpc.decorators import http_to_grpc | ||
from django.views.decorators.vary import vary_on_headers | ||
def vary_on_metadata(*headers): | ||
return http_to_grpc(vary_on_headers(*headers)) | ||
.. _example-with-method-decorator-and-data-variance: | ||
|
||
Example with method decorator and data variance | ||
------------------------------------------------ | ||
|
||
In the following example we are transforming a fucntion decorator into a method decorator. | ||
Then we are transforming it to a grpc decorator. | ||
In the same time we specify that for each simulate Django request we want to set the ``method`` attribute to the value ``GET`` (this is because grpc only use POST request and Django only cache GET request) | ||
|
||
The ``functools.wraps`` is optional and is used to keep the original function name and docstring. | ||
|
||
.. code-block:: python | ||
from django_socio_grpc.decorators import http_to_grpc | ||
from django.views.decorators.cache import cache_page | ||
@functools.wraps(cache_page) | ||
def cache_endpoint(*args, **kwargs): | ||
return http_to_grpc( | ||
method_decorator(cache_page(*args, **kwargs)), | ||
request_setter={"method": "GET"}, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.. _request-and-response-proxy: | ||
|
||
Request and Response proxy | ||
========================== | ||
|
||
Coming soon |