From cabd277b4ad58021c63272babfb0bd669567fc50 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Wed, 29 Jun 2022 14:37:28 +0200 Subject: [PATCH] Update redis, and implement a custom channel layer to send datetime/decimal objects --- gestioasso/settings/cof_prod.py | 2 +- requirements-prod.txt | 2 +- shared/channels.py | 45 +++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 shared/channels.py diff --git a/gestioasso/settings/cof_prod.py b/gestioasso/settings/cof_prod.py index 9bf89781..58496057 100644 --- a/gestioasso/settings/cof_prod.py +++ b/gestioasso/settings/cof_prod.py @@ -148,7 +148,7 @@ CACHES = { CHANNEL_LAYERS = { "default": { - "BACKEND": "channels_redis.core.RedisChannelLayer", + "BACKEND": "shared.channels.ChannelLayer", "CONFIG": { "hosts": [ ( diff --git a/requirements-prod.txt b/requirements-prod.txt index ffd21935..4037e7fe 100644 --- a/requirements-prod.txt +++ b/requirements-prod.txt @@ -5,7 +5,7 @@ psycopg2<2.8 # Redis django-redis-cache==3.0.* -redis~=2.10.6 +redis==3.5.* channels-redis==3.4.* # ASGI protocol and HTTP server diff --git a/shared/channels.py b/shared/channels.py new file mode 100644 index 00000000..ae8c1248 --- /dev/null +++ b/shared/channels.py @@ -0,0 +1,45 @@ +import datetime +import random +from decimal import Decimal + +import msgpack +from channels_redis.core import RedisChannelLayer + + +def encode_kf(obj): + if isinstance(obj, Decimal): + return {"__decimal__": True, "as_str": str(obj)} + elif isinstance(obj, datetime.datetime): + return {"__datetime__": True, "as_str": obj.strftime("%Y%m%dT%H:%M:%S.%f")} + return obj + + +def decode_kf(obj): + if "__decimal__" in obj: + obj = Decimal(obj["as_str"]) + elif "__datetime__" in obj: + obj = datetime.datetime.strptime(obj["as_str"], "%Y%m%dT%H:%M:%S.%f") + return obj + + +class ChannelLayer(RedisChannelLayer): + def serialize(self, message): + """Serializes to a byte string.""" + value = msgpack.packb(message, default=encode_kf, use_bin_type=True) + + if self.crypter: + value = self.crypter.encrypt(value) + + # As we use an sorted set to expire messages + # we need to guarantee uniqueness, with 12 bytes. + random_prefix = random.getrandbits(8 * 12).to_bytes(12, "big") + return random_prefix + value + + def deserialize(self, message): + """Deserializes from a byte string.""" + # Removes the random prefix + message = message[12:] + + if self.crypter: + message = self.crypter.decrypt(message, self.expiry + 10) + return msgpack.unpackb(message, object_hook=decode_kf, raw=False)