feat: implement caching for duty-related data and enhance performance
- Added a TTLCache class for in-memory caching of duty-related data, improving performance by reducing database queries. - Integrated caching into the group duty pin functionality, allowing for efficient retrieval of message text and next shift end times. - Introduced new methods to invalidate caches when relevant data changes, ensuring data consistency. - Created a new Alembic migration to add indexes on the duties table for improved query performance. - Updated tests to cover the new caching behavior and ensure proper functionality.
This commit is contained in:
@@ -5,6 +5,7 @@ from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from duty_teller.cache import duty_pin_cache
|
||||
from duty_teller.db.repository import (
|
||||
get_current_duty,
|
||||
get_next_shift_end,
|
||||
@@ -17,6 +18,31 @@ from duty_teller.i18n import t
|
||||
from duty_teller.utils.dates import parse_utc_iso
|
||||
|
||||
|
||||
def get_pin_refresh_data(
|
||||
session: Session, chat_id: int, tz_name: str, lang: str = "en"
|
||||
) -> tuple[int | None, str, datetime | None]:
|
||||
"""Get all data needed for pin refresh in a single DB session.
|
||||
|
||||
Args:
|
||||
session: DB session.
|
||||
chat_id: Telegram chat id.
|
||||
tz_name: Timezone name for display.
|
||||
lang: Language code for i18n.
|
||||
|
||||
Returns:
|
||||
(message_id, duty_message_text, next_shift_end_utc).
|
||||
message_id is None if no pin record. next_shift_end_utc is naive UTC or None.
|
||||
"""
|
||||
pin = get_group_duty_pin(session, chat_id)
|
||||
message_id = pin.message_id if pin else None
|
||||
if message_id is None:
|
||||
return (None, t(lang, "duty.no_duty"), None)
|
||||
now = datetime.now(timezone.utc)
|
||||
text = get_duty_message_text(session, tz_name, lang)
|
||||
next_end = get_next_shift_end(session, now)
|
||||
return (message_id, text, next_end)
|
||||
|
||||
|
||||
def format_duty_message(duty, user, tz_name: str, lang: str = "en") -> str:
|
||||
"""Build the text for the pinned duty message.
|
||||
|
||||
@@ -64,34 +90,31 @@ def format_duty_message(duty, user, tz_name: str, lang: str = "en") -> str:
|
||||
|
||||
|
||||
def get_duty_message_text(session: Session, tz_name: str, lang: str = "en") -> str:
|
||||
"""Get current duty from DB and return formatted message text.
|
||||
|
||||
Args:
|
||||
session: DB session.
|
||||
tz_name: Timezone name for display.
|
||||
lang: Language code for i18n.
|
||||
|
||||
Returns:
|
||||
Formatted duty message or "No duty" if none.
|
||||
"""
|
||||
"""Get current duty from DB and return formatted message text. Cached 90s."""
|
||||
cache_key = ("duty_message_text", tz_name, lang)
|
||||
text, found = duty_pin_cache.get(cache_key)
|
||||
if found:
|
||||
return text
|
||||
now = datetime.now(timezone.utc)
|
||||
result = get_current_duty(session, now)
|
||||
if result is None:
|
||||
return t(lang, "duty.no_duty")
|
||||
duty, user = result
|
||||
return format_duty_message(duty, user, tz_name, lang)
|
||||
text = t(lang, "duty.no_duty")
|
||||
else:
|
||||
duty, user = result
|
||||
text = format_duty_message(duty, user, tz_name, lang)
|
||||
duty_pin_cache.set(cache_key, text)
|
||||
return text
|
||||
|
||||
|
||||
def get_next_shift_end_utc(session: Session) -> datetime | None:
|
||||
"""Return next shift end as naive UTC datetime for job scheduling.
|
||||
|
||||
Args:
|
||||
session: DB session.
|
||||
|
||||
Returns:
|
||||
Next shift end (naive UTC) or None.
|
||||
"""
|
||||
return get_next_shift_end(session, datetime.now(timezone.utc))
|
||||
"""Return next shift end as naive UTC datetime for job scheduling. Cached 90s."""
|
||||
cache_key = ("next_shift_end",)
|
||||
value, found = duty_pin_cache.get(cache_key)
|
||||
if found:
|
||||
return value
|
||||
result = get_next_shift_end(session, datetime.now(timezone.utc))
|
||||
duty_pin_cache.set(cache_key, result)
|
||||
return result
|
||||
|
||||
|
||||
def save_pin(session: Session, chat_id: int, message_id: int) -> None:
|
||||
|
||||
Reference in New Issue
Block a user