perf: parallelize N+1 player/creator lookups with asyncio.gather (#89)
Closes #89 Replace sequential per-item await loops with asyncio.gather() to fetch all results in parallel: - decision_service.find_winning_losing_pitchers: gather wp, lp, sv, hold_ids, and bsv_ids (5-10 calls) in a single parallel batch - custom_commands_service: parallelize get_creator_by_id() in get_popular_commands, get_commands_needing_warning, and get_commands_eligible_for_deletion using return_exceptions=True to preserve the existing BotException-skip / re-raise-other behavior Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
be4213aab6
commit
aa8330e4d3
@ -4,6 +4,7 @@ Custom Commands Service for Discord Bot v2.0
|
||||
Modern async service layer for managing custom commands with full type safety.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import math
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from typing import Optional, List, Any, Tuple
|
||||
@ -466,21 +467,28 @@ class CustomCommandsService(BaseService[CustomCommand]):
|
||||
|
||||
commands_data = await self.get_items_with_params(params)
|
||||
|
||||
creators = await asyncio.gather(
|
||||
*[
|
||||
self.get_creator_by_id(cmd_data.creator_id)
|
||||
for cmd_data in commands_data
|
||||
],
|
||||
return_exceptions=True,
|
||||
)
|
||||
|
||||
commands = []
|
||||
for cmd_data in commands_data:
|
||||
try:
|
||||
creator = await self.get_creator_by_id(cmd_data.creator_id)
|
||||
commands.append(CustomCommand(**cmd_data.model_dump(), creator=creator))
|
||||
except BotException as e:
|
||||
# Handle missing creator gracefully
|
||||
for cmd_data, creator in zip(commands_data, creators):
|
||||
if isinstance(creator, BotException):
|
||||
self.logger.warning(
|
||||
"Skipping popular command with missing creator",
|
||||
command_id=cmd_data.id,
|
||||
command_name=cmd_data.name,
|
||||
creator_id=cmd_data.creator_id,
|
||||
error=str(e),
|
||||
error=str(creator),
|
||||
)
|
||||
continue
|
||||
if isinstance(creator, BaseException):
|
||||
raise creator
|
||||
commands.append(CustomCommand(**cmd_data.model_dump(), creator=creator))
|
||||
|
||||
return commands
|
||||
|
||||
@ -662,21 +670,28 @@ class CustomCommandsService(BaseService[CustomCommand]):
|
||||
|
||||
commands_data = await self.get_items_with_params(params)
|
||||
|
||||
creators = await asyncio.gather(
|
||||
*[
|
||||
self.get_creator_by_id(cmd_data.creator_id)
|
||||
for cmd_data in commands_data
|
||||
],
|
||||
return_exceptions=True,
|
||||
)
|
||||
|
||||
commands = []
|
||||
for cmd_data in commands_data:
|
||||
try:
|
||||
creator = await self.get_creator_by_id(cmd_data.creator_id)
|
||||
commands.append(CustomCommand(**cmd_data.model_dump(), creator=creator))
|
||||
except BotException as e:
|
||||
# Handle missing creator gracefully
|
||||
for cmd_data, creator in zip(commands_data, creators):
|
||||
if isinstance(creator, BotException):
|
||||
self.logger.warning(
|
||||
"Skipping command with missing creator",
|
||||
command_id=cmd_data.id,
|
||||
command_name=cmd_data.name,
|
||||
creator_id=cmd_data.creator_id,
|
||||
error=str(e),
|
||||
error=str(creator),
|
||||
)
|
||||
continue
|
||||
if isinstance(creator, BaseException):
|
||||
raise creator
|
||||
commands.append(CustomCommand(**cmd_data.model_dump(), creator=creator))
|
||||
|
||||
return commands
|
||||
|
||||
@ -688,21 +703,28 @@ class CustomCommandsService(BaseService[CustomCommand]):
|
||||
|
||||
commands_data = await self.get_items_with_params(params)
|
||||
|
||||
creators = await asyncio.gather(
|
||||
*[
|
||||
self.get_creator_by_id(cmd_data.creator_id)
|
||||
for cmd_data in commands_data
|
||||
],
|
||||
return_exceptions=True,
|
||||
)
|
||||
|
||||
commands = []
|
||||
for cmd_data in commands_data:
|
||||
try:
|
||||
creator = await self.get_creator_by_id(cmd_data.creator_id)
|
||||
commands.append(CustomCommand(**cmd_data.model_dump(), creator=creator))
|
||||
except BotException as e:
|
||||
# Handle missing creator gracefully
|
||||
for cmd_data, creator in zip(commands_data, creators):
|
||||
if isinstance(creator, BotException):
|
||||
self.logger.warning(
|
||||
"Skipping command with missing creator",
|
||||
command_id=cmd_data.id,
|
||||
command_name=cmd_data.name,
|
||||
creator_id=cmd_data.creator_id,
|
||||
error=str(e),
|
||||
error=str(creator),
|
||||
)
|
||||
continue
|
||||
if isinstance(creator, BaseException):
|
||||
raise creator
|
||||
commands.append(CustomCommand(**cmd_data.model_dump(), creator=creator))
|
||||
|
||||
return commands
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ Decision Service
|
||||
Manages pitching decision operations for game submission.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from typing import List, Dict, Any, Optional, Tuple
|
||||
|
||||
from utils.logging import get_contextual_logger
|
||||
@ -124,22 +125,19 @@ class DecisionService:
|
||||
if int(decision.get("b_save", 0)) == 1:
|
||||
bsv_ids.append(pitcher_id)
|
||||
|
||||
# Second pass: Fetch Player objects
|
||||
wp = await player_service.get_player(wp_id) if wp_id else None
|
||||
lp = await player_service.get_player(lp_id) if lp_id else None
|
||||
sv = await player_service.get_player(sv_id) if sv_id else None
|
||||
# Second pass: Fetch all Player objects in parallel
|
||||
# Order: [wp_id, lp_id, sv_id, *hold_ids, *bsv_ids]; None IDs resolve immediately
|
||||
ordered_ids = [wp_id, lp_id, sv_id] + hold_ids + bsv_ids
|
||||
results = await asyncio.gather(
|
||||
*[
|
||||
player_service.get_player(pid) if pid else asyncio.sleep(0, result=None)
|
||||
for pid in ordered_ids
|
||||
]
|
||||
)
|
||||
|
||||
holders = []
|
||||
for hold_id in hold_ids:
|
||||
holder = await player_service.get_player(hold_id)
|
||||
if holder:
|
||||
holders.append(holder)
|
||||
|
||||
blown_saves = []
|
||||
for bsv_id in bsv_ids:
|
||||
bsv = await player_service.get_player(bsv_id)
|
||||
if bsv:
|
||||
blown_saves.append(bsv)
|
||||
wp, lp, sv = results[0], results[1], results[2]
|
||||
holders = [p for p in results[3 : 3 + len(hold_ids)] if p]
|
||||
blown_saves = [p for p in results[3 + len(hold_ids) :] if p]
|
||||
|
||||
return wp, lp, sv, holders, blown_saves
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user