Skip to content

Commit

Permalink
Cache the latest operation status and setting (#35)
Browse files Browse the repository at this point in the history
This resolves #34.

I implemented as follows.
- `ChannelCache` to store the latest operation status and setting for
each channel.
- `utils/util.py` to offer the utility functions to get whether the
specific channel/WLM is currently running.
(Replaced with `get_running_status()` in `operation/views.py`.)

If you aren't familiar with Django signals, please refer to
[docs](https://docs.djangoproject.com/en/5.1/topics/signals/).
  • Loading branch information
BECATRUE authored Nov 28, 2024
2 parents 4e3dc0a + 47b4ab2 commit b3ba5c0
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 25 deletions.
65 changes: 65 additions & 0 deletions wlm_server/cache/channel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""Module for caching the channel status."""

from collections import defaultdict

from operation.models import Operation
from setting.models import Setting

class ChannelCache:
"""Cache for channel status."""

def __init__(self):
# outer key: Channel__channel, inner key: User__username
self._channel_to_operation: defaultdict[int, dict[str, Operation]] = defaultdict(dict)
# key: Channel__channel
self._channel_to_setting: dict[int, Setting] = {}
self._load()

def _load(self):
"""Loads all channels status."""
operations = (Operation.objects.order_by('channel', 'user', '-occurred_at')
.distinct('channel', 'user'))
for operation in operations:
(self._channel_to_operation
[operation.channel.channel][operation.user.username]) = operation
settings = Setting.objects.order_by('channel', '-created_at').distinct('channel')
for setting in settings:
self._channel_to_setting[setting.channel.channel] = setting

def set_operation(self, operation: Operation):
"""Stores the given operation as the latest.
Args:
operation: The latest operation.
"""
self._channel_to_operation[operation.channel.channel][operation.user.username] = operation

def set_setting(self, setting: Setting):
"""Stores the given setting as the latest.
Args:
setting: The latest setting.
"""
self._channel_to_setting[setting.channel.channel] = setting

def get_operations(self, channel: int) -> dict[str, Operation]:
"""Returns the latest operation status for the given channel.
Args:
channel: Target channel.
Returns:
Dictionary with user name as the key and the latest operation status as the value.
"""
return self._channel_to_operation[channel]

def get_setting(self, channel: int) -> Setting:
"""Returns the latest setting for the given channel.
Args:
channel: Target channel.
Returns:
The latest setting.
"""
return self._channel_to_setting[channel]
4 changes: 3 additions & 1 deletion wlm_server/operation/apps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from django.apps import AppConfig


class OperationConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'operation'

def ready(self):
from . import signals # pylint: disable=import-outside-toplevel, unused-import
10 changes: 10 additions & 0 deletions wlm_server/operation/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.conf import settings

from .models import Operation

@receiver(post_save, sender=Operation)
def handle_model_save(sender, **kwargs): # pylint: disable=unused-argument
"""Updates the channel cache whenever an operation status is saved."""
settings.CHANNEL_CACHE.set_operation(kwargs['instance'])
28 changes: 5 additions & 23 deletions wlm_server/operation/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,7 @@
from channel.models import Channel
from task.message import ActionType, MessageInfo, MessageQueue
from task.handler import TaskHandler

def get_running_status(ch: int) -> tuple[bool, bool]:
"""Gets running status of WLM and the given channel.
Args:
ch: Target channel.
Returns:
Tuple with WLM running status and target channel running
"""
latest_operations = (
Operation.objects.order_by('channel', 'user', '-occurred_at').distinct('channel', 'user')
) # latest operations for each channel and user
is_wlm_running = any(op.on for op in latest_operations)
is_channel_running = any(op.channel.channel == ch and op.on for op in latest_operations)
return is_wlm_running, is_channel_running

from utils import util

@login_required
@api_view(['POST'])
Expand All @@ -40,21 +24,19 @@ def handle_info(request, ch: int):
on = req_data['on']
operation = Operation(user=user, channel=channel, on=on)
if on:
is_wlm_running, is_channel_running = get_running_status(ch)
if not is_wlm_running:
if not util.is_wlm_running():
task_handler = TaskHandler()
task_handler.start()
if not is_channel_running:
if not util.is_channel_running(ch):
message = MessageInfo(ActionType.OPERATE, ch, {'on': True})
message_queue.push(message)
operation.save()
else:
operation.save()
is_wlm_running, is_channel_running = get_running_status(ch)
if not is_channel_running:
if not util.is_channel_running(ch):
message = MessageInfo(ActionType.OPERATE, ch, {'on': False})
message_queue.push(message)
if not is_wlm_running:
if not util.is_wlm_running():
message = MessageInfo(ActionType.CLOSE, None, None)
message_queue.push(message)
return HttpResponse(status=200)
3 changes: 3 additions & 0 deletions wlm_server/setting/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
class SettingConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'setting'

def ready(self):
from . import signals # pylint: disable=import-outside-toplevel, unused-import
10 changes: 10 additions & 0 deletions wlm_server/setting/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.conf import settings

from .models import Setting

@receiver(post_save, sender=Setting)
def handle_model_save(sender, **kwargs): # pylint: disable=unused-argument
"""Updates the channel cache whenever an operation status is saved."""
settings.CHANNEL_CACHE.set_setting(kwargs['instance'])
2 changes: 1 addition & 1 deletion wlm_server/setting/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def handle_info(request, ch: int):
if not channel.teams.contains(user.team):
return HttpResponse(status=403)
message_queue: MessageQueue = settings.MESSAGE_QUEUE
latest_setting = Setting.objects.filter(channel__channel=ch).order_by('-created_at').first()
latest_setting = settings.CHANNEL_CACHE.get_setting(ch)
if latest_setting is None:
exposure, period = None, None
else:
Expand Down
17 changes: 17 additions & 0 deletions wlm_server/utils/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from django.conf import settings

from channel.models import Channel

def is_channel_running(channel: int) -> bool:
"""Returns whether the given channel is currently running.
Args:
channel: Target channel.
"""
operations = settings.CHANNEL_CACHE.get_operations(channel)
return any(op.on for op in operations.values())


def is_wlm_running() -> bool:
"""Returns whether the WLM is currently running."""
return any(is_channel_running(channel.channel) for channel in Channel.objects.all())
2 changes: 2 additions & 0 deletions wlm_server/wlm_server/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,5 @@
AUTH_USER_MODEL = 'user.User'

MESSAGE_QUEUE = MessageQueue()

CHANNEL_CACHE = None
5 changes: 5 additions & 0 deletions wlm_server/wlm_server/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@

import os

from django.conf import settings
from django.core.wsgi import get_wsgi_application

from cache.channel import ChannelCache

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'wlm_server.settings')

settings.CHANNEL_CACHE = ChannelCache()

application = get_wsgi_application()

0 comments on commit b3ba5c0

Please sign in to comment.