.. _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 `_. 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 `_ then use the :ref:`cache_endpoint ` decorator or the :ref:`cache_endpoint_with_deleter ` to cache your endpoint. .. _cache-endpoint: cache_endpoint -------------- Th :func:`cache_endpoint ` decorator is used to adapt the `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 ` for more informations. This decorator will cache response depending on: * :ref:`Filters ` * :ref:`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 ` decorator work the same :ref:`cache_endpoint ` but allow to automatically delete the cache when a django signals is called from the models passed in parameters or the one used for the queryset if not specified. 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 `_ The cache will also not be deleted if modifying date in an other process that is not gRPC (Django commands, admin, shell, ...). You can make your own decorator to handle this case if needed by registering decorator parameter in a global context and then listen to all django event to see if one matching. We decide to not integrate it because listening all django events and making check on signals and senders may add an unwanted request overhead. 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 `_ * Use a cache per model .. note:: If you do not follow above advice a warning will show up everytimes you start the server. You can mute the logger by muting the django_socio_grpc.cache_deleter logger in your logging settings. 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, cache="UnitTestModelCache", # Cache is not mandatory. But it is for working as expecting is using any other cache system than redis. # key_prefix="UnitTestModel-List", # You can specify a key prefix if needed. It will allow you to use a specific pattern for cache action. Default is - # senders=(UnitTestModel,), # You can specify a list of models to listen to. Default is the queryset model. # signals=(signals.post_save, signals.post_delete), # You can specify a list of signals to listen to. Default is (signals.post_save, signals.post_delete) ) async def List(self, request, context): return await super().List(request, context) .. _vary-on-metadata: vary_on_metadata ---------------- Working like django `vary_on_headers `_ it's just a convenient renaming using :ref:`Use Django decorators in DSG `. It allow the cache to also `vary on 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 `. You can try to use any django decorators as long as they are wrapped into :func:`http_to_grpc decorator `. 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.