From 6f6bd2ad965b8cac26270b9e4e95efdd7f4ec2de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Wed, 24 Jan 2024 18:03:16 +0100 Subject: [PATCH] perf: cache BBB API calls --- web/b3desk/models/bbb.py | 13 +++ web/b3desk/models/meetings.py | 3 +- web/b3desk/settings.py | 4 + web/tests/conftest.py | 9 +- web/tests/test_bbb_api_caching.py | 150 ++++++++++++++++++++++++++++++ 5 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 web/tests/test_bbb_api_caching.py diff --git a/web/b3desk/models/bbb.py b/web/b3desk/models/bbb.py index 60c38582..8325e680 100644 --- a/web/b3desk/models/bbb.py +++ b/web/b3desk/models/bbb.py @@ -18,6 +18,13 @@ from flask import current_app from flask import render_template +from .. import cache + + +def caching_exclusion(*args, **kwargs): + """Only GET methods should be cached.""" + return kwargs.get("method", "GET") != "GET" + class BBB: """Interface to BBB API.""" @@ -43,6 +50,10 @@ def bbb_request(self, action, method="GET", **kwargs): prepped.prepare_url(prepped.url, params={"checksum": checksum}) return prepped + @cache.memoize( + unless=caching_exclusion, + timeout=current_app.config["BIGBLUEBUTTON_API_CACHE_DURATION"], + ) def bbb_response(self, request): session = requests.Session() current_app.logger.debug("BBB API request %s: %s", request.method, request.url) @@ -50,6 +61,7 @@ def bbb_response(self, request): return {c.tag: c.text for c in ElementTree.fromstring(response.content)} def is_meeting_running(self): + """https://docs.bigbluebutton.org/development/api/#ismeetingrunning""" request = self.bbb_request( "isMeetingRunning", params={"meetingID": self.meeting.meetingID} ) @@ -239,6 +251,7 @@ def get_meeting_info(self): ) return self.bbb_response(request) + @cache.memoize(timeout=current_app.config["BIGBLUEBUTTON_API_CACHE_DURATION"]) def get_recordings(self): """https://docs.bigbluebutton.org/development/api/#getrecordings""" request = self.bbb_request( diff --git a/web/b3desk/models/meetings.py b/web/b3desk/models/meetings.py index 62f3705b..60184f19 100644 --- a/web/b3desk/models/meetings.py +++ b/web/b3desk/models/meetings.py @@ -19,7 +19,6 @@ from sqlalchemy_utils import StringEncryptedType from . import db -from .bbb import BBB from .users import User MODERATOR_ONLY_MESSAGE_MAXLENGTH = 150 @@ -109,6 +108,8 @@ class Meeting(db.Model): @property def bbb(self): + from .bbb import BBB + if not self._bbb: self._bbb = BBB(self) return self._bbb diff --git a/web/b3desk/settings.py b/web/b3desk/settings.py index fcf782f9..f2685e95 100644 --- a/web/b3desk/settings.py +++ b/web/b3desk/settings.py @@ -976,3 +976,7 @@ def get_rie_network_ips( Plus d’informations sur https://docs.bigbluebutton.org/development/api/#create """ + + BIGBLUEBUTTON_API_CACHE_DURATION: int = 5 + """Le temps de mise en cache (en secondes) des réponses aux requêtes GET à + l'API BBB.""" diff --git a/web/tests/conftest.py b/web/tests/conftest.py index e9fbbb57..17f9de82 100644 --- a/web/tests/conftest.py +++ b/web/tests/conftest.py @@ -15,8 +15,6 @@ b3desk.utils.secret_key = lambda: "AZERTY" from b3desk.models import db -from b3desk.models.meetings import Meeting -from b3desk.models.users import User @pytest.fixture @@ -65,6 +63,9 @@ def configuration(tmp_path, iam_server, iam_client): "FILE_SHARING": True, # Overwrite the web.env values for tests running in docker "STATS_URL": None, + # Disable cache in unit tests + "CACHE_DEFAULT_TIMEOUT": 0, + "BIGBLUEBUTTON_API_CACHE_DURATION": 0, } @@ -86,6 +87,8 @@ def client_app(app): @pytest.fixture def meeting(client_app, user): + from b3desk.models.meetings import Meeting + meeting = Meeting(user=user) meeting.save() @@ -94,6 +97,8 @@ def meeting(client_app, user): @pytest.fixture def user(client_app): + from b3desk.models.users import User + user = User(email="alice@domain.tld", given_name="Alice", family_name="Cooper") user.save() diff --git a/web/tests/test_bbb_api_caching.py b/web/tests/test_bbb_api_caching.py new file mode 100644 index 00000000..9d221f4d --- /dev/null +++ b/web/tests/test_bbb_api_caching.py @@ -0,0 +1,150 @@ +import pytest + + +@pytest.fixture +def configuration(configuration): + configuration["BIGBLUEBUTTON_API_CACHE_DURATION"] = 5 + return configuration + + +IS_MEETING_RUNNING_SUCCESS_RESPONSE = """ + + SUCCESS + true + +""" + + +def test_is_meeting_running(meeting, mocker): + class Response: + content = IS_MEETING_RUNNING_SUCCESS_RESPONSE + + get = mocker.patch("requests.Session.send", return_value=Response) + + assert get.call_count == 0 + + assert meeting.bbb.is_meeting_running() + assert get.call_count == 1 + + assert meeting.bbb.is_meeting_running() + assert get.call_count == 1 + + +GET_RECORDINGS_RESPONSE = """ + + SUCCESS + + + ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124 + c637ba21adcd0191f48f5c4bf23fab0f96ed5c18 + ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124 + Fred's Room + false + true + published + 1530718721124 + 1530718810456 + 3 + 951067 + + https://bbb-analytics.url + false + c637ba21adcd0191f48f5c4bf23fab0f96ed5c18 + Fred's Room + + + unknown + 0 + false + + 1104836 + + + presentation + https://demo.bigbluebutton.org/playback/presentation/2.0/playback.html?meetingId=ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124 + 7177 + 0 + 1104836 + + + Welcome tohttps://demo.bigbluebutton.org/presentation/ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124/presentation/d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1530718721134/thumbnails/thumb-1.png + (this slide left blank for use as a whiteboard)https://demo.bigbluebutton.org/presentation/ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124/presentation/d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1530718721134/thumbnails/thumb-2.png + (this slide left blank for use as a whiteboard)https://demo.bigbluebutton.org/presentation/ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124/presentation/d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1530718721134/thumbnails/thumb-3.png + + + + + video + https://demo.bigbluebutton.org/podcast/ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124/meeting.mp4 + 0 + 0 + 1104836 + + + + + ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111 + c637ba21adcd0191f48f5c4bf23fab0f96ed5c18 + ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111 + Fred's Room + false + true + published + 1530278898111 + 1530281194326 + 7 + 381530 + + Recording title hand written + Fred's Room + c637ba21adcd0191f48f5c4bf23fab0f96ed5c18 + https://bbb-analytics.url + false + + + unknown + 0 + false + + + + podcast + https://demo.bigbluebutton.org/podcast/ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111/audio.ogg + 0 + 33 + + + presentation + https://demo.bigbluebutton.org/playback/presentation/2.0/playback.html?meetingId=ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111 + 139458 + 33 + + + Welcome tohttps://demo.bigbluebutton.org/presentation/ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111/presentation/d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1530278898120/thumbnails/thumb-1.png + (this slide left blank for use as a whiteboard)https://demo.bigbluebutton.org/presentation/ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111/presentation/d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1530278898120/thumbnails/thumb-2.png + (this slide left blank for use as a whiteboard)https://demo.bigbluebutton.org/presentation/ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111/presentation/d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1530278898120/thumbnails/thumb-3.png + + + + + + + +""" + + +def test_get_recordings(meeting, mocker): + class Response: + content = GET_RECORDINGS_RESPONSE + + get = mocker.patch("requests.Session.send", return_value=Response) + + assert get.call_count == 0 + + recordings = meeting.bbb.get_recordings() + assert len(recordings) == 2 + assert get.call_count == 1 + + recordings = meeting.bbb.get_recordings() + assert len(recordings) == 2 + assert get.call_count == 1