Skip to content

Commit 46af39a

Browse files
committed
Use redis to store the role cache
1 parent 5023467 commit 46af39a

File tree

4 files changed

+16
-37
lines changed

4 files changed

+16
-37
lines changed

backend/authentication/backend.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,6 @@ async def authenticate(
6868
if await user.fetch_admin_status(request.state.db):
6969
scopes.append("admin")
7070

71-
scopes.extend(await user.get_user_roles(request.state.db))
71+
scopes.extend(await user.get_user_roles())
7272

7373
return authentication.AuthCredentials(scopes), user

backend/authentication/user.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@ def user_id(self) -> str:
4444
def decoded_token(self) -> dict[str, any]:
4545
return jwt.decode(self.token, SECRET_KEY, algorithms=["HS256"])
4646

47-
async def get_user_roles(self, database: Database) -> list[str]:
47+
async def get_user_roles(self) -> list[str]:
4848
"""Get a list of the user's discord roles."""
4949
if not self.member:
5050
return []
5151

52-
server_roles = await discord.get_roles(database)
52+
server_roles = await discord.get_roles()
5353
roles = [role.name for role in server_roles if role.id in self.member.roles]
5454

5555
if "admin" in roles:

backend/discord.py

+11-32
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
"""Various utilities for working with the Discord API."""
22

3-
import datetime
43
import json
54

65
import httpx
76
import starlette.requests
8-
from pymongo.database import Database
97
from starlette import exceptions
108

119
from backend import constants, models
@@ -66,7 +64,6 @@ async def _get_role_info() -> list[models.DiscordRole]:
6664

6765

6866
async def get_roles(
69-
database: Database,
7067
*,
7168
force_refresh: bool = False,
7269
) -> list[models.DiscordRole]:
@@ -75,35 +72,17 @@ async def get_roles(
7572
7673
If `force_refresh` is True, the cache is skipped and the roles are updated.
7774
"""
78-
collection = database.get_collection("roles")
79-
80-
if force_refresh:
81-
# Drop all values in the collection
82-
await collection.delete_many({})
83-
84-
# `create_index` creates the index if it does not exist, or passes
85-
# This handles TTL on role objects
86-
await collection.create_index(
87-
"inserted_at",
88-
expireAfterSeconds=60 * 60 * 24, # 1 day
89-
name="inserted_at",
90-
)
91-
92-
roles = [models.DiscordRole(**json.loads(role["data"])) async for role in collection.find()]
93-
94-
if len(roles) == 0:
95-
# Fetch roles from the API and insert into the database
96-
roles = await _get_role_info()
97-
await collection.insert_many(
98-
{
99-
"name": role.name,
100-
"id": role.id,
101-
"data": role.json(),
102-
"inserted_at": datetime.datetime.now(tz=datetime.UTC),
103-
}
104-
for role in roles
105-
)
106-
75+
role_cache_key = "forms-backend:role_cache"
76+
if not force_refresh:
77+
roles = await constants.REDIS_CLIENT.hgetall(role_cache_key)
78+
if roles:
79+
return [
80+
models.DiscordRole(**json.loads(role_data)) for role_id, role_data in roles.items()
81+
]
82+
83+
roles = await _get_role_info()
84+
await constants.REDIS_CLIENT.hmset(role_cache_key, {role.id: role.json() for role in roles})
85+
await constants.REDIS_CLIENT.expire(role_cache_key, 60 * 60 * 24) # 1 day
10786
return roles
10887

10988

backend/routes/discord.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ class RolesResponse(pydantic.BaseModel):
3131
resp=Response(HTTP_200=RolesResponse),
3232
tags=["roles"],
3333
)
34-
async def patch(self, request: Request) -> JSONResponse:
34+
async def patch(self, request: Request) -> JSONResponse: # noqa: ARG002 Request is required by @requires
3535
"""Refresh the roles database."""
36-
roles = await discord.get_roles(request.state.db, force_refresh=True)
36+
roles = await discord.get_roles(force_refresh=True)
3737

3838
return JSONResponse(
3939
{"roles": [role.dict() for role in roles]},

0 commit comments

Comments
 (0)