Files
musicseerr/backend/infrastructure/queue/priority_queue.py
T
Harvey a69a26852e Cut down unnecessary logging (#48)
* Cut down unnecessary logging

* fix format etc

* fix checks

* fix tests
2026-04-14 00:02:38 +01:00

97 lines
3.2 KiB
Python

import asyncio
from enum import IntEnum
from typing import Optional
from datetime import datetime
class RequestPriority(IntEnum):
USER_INITIATED = 0
IMAGE_FETCH = 1
PREFETCH_VISIBLE = 2
BACKGROUND_SYNC = 3
OPPORTUNISTIC = 4
class PriorityQueueManager:
_instance: Optional['PriorityQueueManager'] = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self):
if self._initialized:
return
self._user_semaphore = asyncio.Semaphore(20)
self._image_semaphore = asyncio.Semaphore(10)
self._background_semaphore = asyncio.Semaphore(5)
self._user_activity_flag = False
self._user_activity_timestamp = 0.0
self._user_activity_timeout = 2.0
self._user_activity_event = asyncio.Event()
self._background_waiters = 0
self._initialized = True
async def acquire_slot(self, priority: RequestPriority) -> asyncio.Semaphore:
if priority == RequestPriority.USER_INITIATED:
self._mark_user_activity()
return self._user_semaphore
elif priority == RequestPriority.IMAGE_FETCH:
return self._image_semaphore
else:
await self._wait_for_user_inactivity()
return self._background_semaphore
def _mark_user_activity(self):
self._user_activity_flag = True
self._user_activity_timestamp = datetime.now().timestamp()
self._user_activity_event.clear()
async def _wait_for_user_inactivity(self):
self._background_waiters += 1
try:
while self._user_activity_flag:
current = datetime.now().timestamp()
elapsed = current - self._user_activity_timestamp
if elapsed >= self._user_activity_timeout:
self._user_activity_flag = False
self._user_activity_event.set()
break
wait_time = self._user_activity_timeout - elapsed
try:
await asyncio.wait_for(
self._user_activity_event.wait(),
timeout=wait_time + 0.1
)
except asyncio.TimeoutError:
pass
finally:
self._background_waiters -= 1
def mark_user_activity(self):
self._mark_user_activity()
def is_user_active(self) -> bool:
current = datetime.now().timestamp()
if current - self._user_activity_timestamp > self._user_activity_timeout:
self._user_activity_flag = False
return self._user_activity_flag
def get_stats(self) -> dict:
return {
'user_slots_available': self._user_semaphore._value,
'image_slots_available': self._image_semaphore._value,
'background_slots_available': self._background_semaphore._value,
'user_active': self.is_user_active(),
'background_waiters': self._background_waiters
}
def get_priority_queue() -> PriorityQueueManager:
return PriorityQueueManager()