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:
@@ -4,10 +4,12 @@ from datetime import date, timedelta
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from duty_teller.cache import invalidate_duty_related_caches
|
||||
from duty_teller.db.models import Duty
|
||||
from duty_teller.db.repository import (
|
||||
get_or_create_user_by_full_name,
|
||||
delete_duties_in_range,
|
||||
insert_duty,
|
||||
get_or_create_user_by_full_name,
|
||||
get_users_by_full_names,
|
||||
)
|
||||
from duty_teller.importers.duty_schedule import DutyScheduleResult
|
||||
from duty_teller.utils.dates import day_start_iso, day_end_iso, duty_to_iso
|
||||
@@ -37,11 +39,10 @@ def run_import(
|
||||
hour_utc: int,
|
||||
minute_utc: int,
|
||||
) -> tuple[int, int, int, int]:
|
||||
"""Run duty-schedule import: delete range per user, insert duty/unavailable/vacation.
|
||||
"""Run duty-schedule import: delete range per user, bulk insert duties.
|
||||
|
||||
For each entry: get_or_create_user_by_full_name, delete_duties_in_range for
|
||||
the result date range, then insert duties (handover time in UTC), unavailable
|
||||
(all-day), and vacation (consecutive ranges).
|
||||
Batched: users fetched in one query, missing created; bulk_insert_mappings.
|
||||
One commit at end.
|
||||
|
||||
Args:
|
||||
session: DB session.
|
||||
@@ -55,31 +56,61 @@ def run_import(
|
||||
from_date_str = result.start_date.isoformat()
|
||||
to_date_str = result.end_date.isoformat()
|
||||
num_duty = num_unavailable = num_vacation = 0
|
||||
|
||||
# Batch: get all users by full_name, create missing
|
||||
names = [e.full_name for e in result.entries]
|
||||
users_map = get_users_by_full_names(session, names)
|
||||
for name in names:
|
||||
if name not in users_map:
|
||||
users_map[name] = get_or_create_user_by_full_name(session, name)
|
||||
|
||||
# Delete range per user (no commit)
|
||||
for entry in result.entries:
|
||||
user = get_or_create_user_by_full_name(session, entry.full_name)
|
||||
delete_duties_in_range(session, user.id, from_date_str, to_date_str)
|
||||
user = users_map[entry.full_name]
|
||||
delete_duties_in_range(
|
||||
session, user.id, from_date_str, to_date_str, commit=False
|
||||
)
|
||||
|
||||
# Build rows for bulk insert
|
||||
duty_rows: list[dict] = []
|
||||
for entry in result.entries:
|
||||
user = users_map[entry.full_name]
|
||||
for d in entry.duty_dates:
|
||||
start_at = duty_to_iso(d, hour_utc, minute_utc)
|
||||
d_next = d + timedelta(days=1)
|
||||
end_at = duty_to_iso(d_next, hour_utc, minute_utc)
|
||||
insert_duty(session, user.id, start_at, end_at, event_type="duty")
|
||||
duty_rows.append(
|
||||
{
|
||||
"user_id": user.id,
|
||||
"start_at": start_at,
|
||||
"end_at": end_at,
|
||||
"event_type": "duty",
|
||||
}
|
||||
)
|
||||
num_duty += 1
|
||||
for d in entry.unavailable_dates:
|
||||
insert_duty(
|
||||
session,
|
||||
user.id,
|
||||
day_start_iso(d),
|
||||
day_end_iso(d),
|
||||
event_type="unavailable",
|
||||
duty_rows.append(
|
||||
{
|
||||
"user_id": user.id,
|
||||
"start_at": day_start_iso(d),
|
||||
"end_at": day_end_iso(d),
|
||||
"event_type": "unavailable",
|
||||
}
|
||||
)
|
||||
num_unavailable += 1
|
||||
for start_d, end_d in _consecutive_date_ranges(entry.vacation_dates):
|
||||
insert_duty(
|
||||
session,
|
||||
user.id,
|
||||
day_start_iso(start_d),
|
||||
day_end_iso(end_d),
|
||||
event_type="vacation",
|
||||
duty_rows.append(
|
||||
{
|
||||
"user_id": user.id,
|
||||
"start_at": day_start_iso(start_d),
|
||||
"end_at": day_end_iso(end_d),
|
||||
"event_type": "vacation",
|
||||
}
|
||||
)
|
||||
num_vacation += 1
|
||||
|
||||
if duty_rows:
|
||||
session.bulk_insert_mappings(Duty, duty_rows)
|
||||
session.commit()
|
||||
invalidate_duty_related_caches()
|
||||
return (len(result.entries), num_duty, num_unavailable, num_vacation)
|
||||
|
||||
Reference in New Issue
Block a user