From 87afc439fe7451a1178b68b1bbee3efab66766aa Mon Sep 17 00:00:00 2001 From: Chris Hill-Scott Date: Mon, 22 Jun 2020 09:39:46 +0100 Subject: [PATCH] Cache serialised template in memory --- app/serialised_models.py | 25 +++++++++++++++++ .../notifications/test_post_notifications.py | 28 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/app/serialised_models.py b/app/serialised_models.py index f864171f7..1896c1810 100644 --- a/app/serialised_models.py +++ b/app/serialised_models.py @@ -1,7 +1,31 @@ from abc import ABC, abstractmethod +from collections import defaultdict +from functools import partial +from threading import RLock + +import cachetools from app.dao import templates_dao +caches = defaultdict(partial(cachetools.TTLCache, maxsize=1024, ttl=2)) +locks = defaultdict(RLock) + + +def memory_cache(func): + @cachetools.cached( + cache=caches[func.__qualname__], + lock=locks[func.__qualname__], + key=ignore_first_argument_cache_key, + ) + def wrapper(*args, **kwargs): + return func(*args, **kwargs) + + return wrapper + + +def ignore_first_argument_cache_key(cls, *args, **kwargs): + return cachetools.keys.hashkey(*args, **kwargs) + class SerialisedModel(ABC): @@ -40,6 +64,7 @@ class SerialisedTemplate(SerialisedModel): } @classmethod + @memory_cache def from_id_and_service_id(cls, template_id, service_id): return cls(cls.get_dict(template_id, service_id)) diff --git a/tests/app/v2/notifications/test_post_notifications.py b/tests/app/v2/notifications/test_post_notifications.py index 7fe8acc0a..4348ae24d 100644 --- a/tests/app/v2/notifications/test_post_notifications.py +++ b/tests/app/v2/notifications/test_post_notifications.py @@ -6,6 +6,7 @@ import pytest from freezegun import freeze_time from boto.exception import SQSError +from app.dao import templates_dao from app.dao.service_sms_sender_dao import dao_update_service_sms_sender from app.models import ( ScheduledNotification, @@ -211,6 +212,33 @@ def test_notification_reply_to_text_is_original_value_if_sender_is_changed_after assert notifications[0].reply_to_text == '123456' +def test_should_cache_template_lookups_in_memory(mocker, client, sample_template): + + mock_get_template = mocker.patch( + 'app.dao.templates_dao.dao_get_template_by_id_and_service_id', + wraps=templates_dao.dao_get_template_by_id_and_service_id, + ) + mocker.patch('app.celery.provider_tasks.deliver_sms.apply_async') + + data = { + 'phone_number': '+447700900855', + 'template_id': str(sample_template.id), + } + + for i in range(5): + auth_header = create_authorization_header(service_id=sample_template.service_id) + client.post( + path='/v2/notifications/sms', + data=json.dumps(data), + headers=[('Content-Type', 'application/json'), auth_header] + ) + + assert mock_get_template.call_args_list == [ + call(service_id=sample_template.service_id, template_id=str(sample_template.id)) + ] + assert Notification.query.count() == 5 + + @pytest.mark.parametrize("notification_type, key_send_to, send_to", [("sms", "phone_number", "+447700900855"), ("email", "email_address", "sample@email.com")])