Skip to content

Commit

Permalink
perf: cache BBB API calls
Browse files Browse the repository at this point in the history
  • Loading branch information
azmeuk committed Jan 26, 2024
1 parent c98bf0f commit 6f6bd2a
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 3 deletions.
13 changes: 13 additions & 0 deletions web/b3desk/models/bbb.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand All @@ -43,13 +50,18 @@ 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)
response = session.send(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}
)
Expand Down Expand Up @@ -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(
Expand Down
3 changes: 2 additions & 1 deletion web/b3desk/models/meetings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions web/b3desk/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
9 changes: 7 additions & 2 deletions web/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
}


Expand All @@ -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()

Expand All @@ -94,6 +97,8 @@ def meeting(client_app, user):

@pytest.fixture
def user(client_app):
from b3desk.models.users import User

user = User(email="[email protected]", given_name="Alice", family_name="Cooper")
user.save()

Expand Down
150 changes: 150 additions & 0 deletions web/tests/test_bbb_api_caching.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import pytest


@pytest.fixture
def configuration(configuration):
configuration["BIGBLUEBUTTON_API_CACHE_DURATION"] = 5
return configuration


IS_MEETING_RUNNING_SUCCESS_RESPONSE = """
<response>
<returncode>SUCCESS</returncode>
<running>true</running>
</response>
"""


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 = """
<response>
<returncode>SUCCESS</returncode>
<recordings>
<recording>
<recordID>ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124</recordID>
<meetingID>c637ba21adcd0191f48f5c4bf23fab0f96ed5c18</meetingID>
<internalMeetingID>ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124</internalMeetingID>
<name>Fred's Room</name>
<isBreakout>false</isBreakout>
<published>true</published>
<state>published</state>
<startTime>1530718721124</startTime>
<endTime>1530718810456</endTime>
<participants>3</participants>
<rawSize>951067</rawSize>
<metadata>
<analytics-callback-url>https://bbb-analytics.url</analytics-callback-url>
<isBreakout>false</isBreakout>
<meetingId>c637ba21adcd0191f48f5c4bf23fab0f96ed5c18</meetingId>
<meetingName>Fred's Room</meetingName>
</metadata>
<breakout>
<parentId>unknown</parentId>
<sequence>0</sequence>
<freeJoin>false</freeJoin>
</breakout>
<size>1104836</size>
<playback>
<format>
<type>presentation</type>
<url>https://demo.bigbluebutton.org/playback/presentation/2.0/playback.html?meetingId=ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124</url>
<processingTime>7177</processingTime>
<length>0</length>
<size>1104836</size>
<preview>
<images>
<image alt="Welcome to" height="136" width="176">https://demo.bigbluebutton.org/presentation/ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124/presentation/d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1530718721134/thumbnails/thumb-1.png</image>
<image alt="(this slide left blank for use as a whiteboard)" height="136" width="176">https://demo.bigbluebutton.org/presentation/ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124/presentation/d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1530718721134/thumbnails/thumb-2.png</image>
<image alt="(this slide left blank for use as a whiteboard)" height="136" width="176">https://demo.bigbluebutton.org/presentation/ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124/presentation/d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1530718721134/thumbnails/thumb-3.png</image>
</images>
</preview>
</format>
<format>
<type>video</type>
<url>https://demo.bigbluebutton.org/podcast/ffbfc4cc24428694e8b53a4e144f414052431693-1530718721124/meeting.mp4</url>
<processingTime>0</processingTime>
<length>0</length>
<size>1104836</size>
</format>
</playback>
</recording>
<recording>
<recordID>ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111</recordID>
<meetingID>c637ba21adcd0191f48f5c4bf23fab0f96ed5c18</meetingID>
<internalMeetingID>ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111</internalMeetingID>
<name>Fred's Room</name>
<isBreakout>false</isBreakout>
<published>true</published>
<state>published</state>
<startTime>1530278898111</startTime>
<endTime>1530281194326</endTime>
<participants>7</participants>
<rawSize>381530</rawSize>
<metadata>
<name>Recording title hand written</name>
<meetingName>Fred's Room</meetingName>
<meetingId>c637ba21adcd0191f48f5c4bf23fab0f96ed5c18</meetingId>
<analytics-callback-url>https://bbb-analytics.url</analytics-callback-url>
<isBreakout>false</isBreakout>
</metadata>
<breakout>
<parentId>unknown</parentId>
<sequence>0</sequence>
<freeJoin>false</freeJoin>
</breakout>
<playback>
<format>
<type>podcast</type>
<url>https://demo.bigbluebutton.org/podcast/ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111/audio.ogg</url>
<processingTime>0</processingTime>
<length>33</length>
</format>
<format>
<type>presentation</type>
<url>https://demo.bigbluebutton.org/playback/presentation/2.0/playback.html?meetingId=ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111</url>
<processingTime>139458</processingTime>
<length>33</length>
<preview>
<images>
<image width="176" height="136" alt="Welcome to">https://demo.bigbluebutton.org/presentation/ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111/presentation/d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1530278898120/thumbnails/thumb-1.png</image>
<image width="176" height="136" alt="(this slide left blank for use as a whiteboard)">https://demo.bigbluebutton.org/presentation/ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111/presentation/d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1530278898120/thumbnails/thumb-2.png</image>
<image width="176" height="136" alt="(this slide left blank for use as a whiteboard)">https://demo.bigbluebutton.org/presentation/ffbfc4cc24428694e8b53a4e144f414052431693-1530278898111/presentation/d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1530278898120/thumbnails/thumb-3.png</image>
</images>
</preview>
</format>
</playback>
</recording>
</recordings>
</response>
"""


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

0 comments on commit 6f6bd2a

Please sign in to comment.