perf: parallelize N+1 player/creator lookups with asyncio.gather (#89) #118
No reviewers
Labels
No Label
ai-changes-requested
ai-pr-opened
ai-reviewed
ai-reviewing
ai-working
in-next-release
status/in-progress
status/pr-open
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: cal/major-domo-v2#118
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "ai/major-domo-v2-89"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #89
Summary
Replaces sequential per-item
awaitloops withasyncio.gather()to fire all independent API lookups in parallel.services/decision_service.py—find_winning_losing_pitchersBefore: wp, lp, sv, hold_ids, and bsv_ids were fetched one-at-a-time (5–10 sequential calls per game).
After: all IDs are gathered into a single ordered list
[wp_id, lp_id, sv_id, *hold_ids, *bsv_ids]and fetched in oneasyncio.gather(). None-IDs useasyncio.sleep(0, result=None)as an immediately-resolving no-op. Results are sliced back intowp,lp,sv,holders,blown_savesby position.services/custom_commands_service.py— three methodsget_popular_commands,get_commands_needing_warning,get_commands_eligible_for_deletioneach looped over a command list callingget_creator_by_id()sequentially.After: all creator lookups are gathered in parallel with
return_exceptions=True. The post-gather loop preserves original behavior:BotException→ log warning and skip (missing creator)BaseException→ re-raise (unexpected error, same as the originaltry/except BotExceptionpropagating non-BotException errors)Files Changed
services/decision_service.pyservices/custom_commands_service.pyTest Results
977 passed, 2 skipped
Other observations
creator_idfield on the command model is typedOptional[int]butget_creator_by_idexpectsint— pre-existing Pyright issue, not introduced here.AI Code Review
Files Reviewed
services/decision_service.py(modified)services/custom_commands_service.py(modified)Findings
Correctness
No issues found.
decision_service.py—find_winning_losing_pitchers:ordered_ids = [wp_id, lp_id, sv_id] + hold_ids + bsv_idsbuilds the gather input in documented order.asyncio.gatherpreserves order, soresults[0..2]→ wp/lp/sv is correct.results[3 : 3 + len(hold_ids)]andresults[3 + len(hold_ids) :]slice correctly for holders and blown_saves.asyncio.sleep(0, result=None)is the correct stdlib no-op for None IDs — immediately resolves toNone, matching the originalif pid else Nonesemantics.if pfilter on holders/blown_saves preserves original behavior.custom_commands_service.py— three cleanup methods:return_exceptions=Trueis correct — exceptions become values in the results list.BotExceptionchecked first (log + skip), thenBaseExceptionre-raised. This is semantically equivalent to the originaltry/except BotException(non-BotException propagated in both versions).zip(commands_data, creators)is safe;asyncio.gatherorder matches input order.Security
No issues found.
Style & Conventions
No issues found. Pattern is consistent with existing parallelization in this codebase (PR #103, #106).
Suggestions
Optional[int]vsinttype mismatch oncreator_idis pre-existing and correctly noted in the PR body. Worth a follow-up Pyright pass.custom_commands_service.pyare pre-existing structural similarity — not introduced here, not worth refactoring in this PR.Verdict: COMMENT
Implementation is correct. Both gather patterns are semantically equivalent to their sequential predecessors. Exception handling semantics preserved. 977 tests passing. Approve when ready.
Automated review by Claude PR Reviewer
aa8330e4d3to65d3099a7c