perf: replace sequential awaits with asyncio.gather() #102
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#102
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "fix/sequential-awaits"
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?
Summary
asyncio.gather()incommands/transactions/management.py(3 transaction fetches) andservices/stats_service.py(batting + pitching stats)tests/test_services_stats.pywith 5 tests coveringget_player_stats()(both stats, one stat, no stats, exception handling)Fixes #87
Test plan
🤖 Generated with Claude Code
Review: perf: replace sequential awaits with asyncio.gather()
Verdict: APPROVED (posted as COMMENT — Gitea blocks self-approval)
Summary
The PR correctly identifies and fixes a false-parallel pattern in two places. Variables named
_taskwere storing coroutine objects (notasyncio.Taskobjects), so the three sequentialawaitcalls in/mymovesand the two inget_player_statswere executing serially despite the naming suggesting otherwise.asyncio.gather()is the right fix and will genuinely parallelize these API calls.Correctness
commands/transactions/management.py—my_movesgathers 3 transaction fetches concurrently. ✅ The pre-existing gather calls in/legalconfirm this pattern was already established; this PR correctly extends it tomymoves. The conditionalget_team_transactions(cancelled=True)remaining sequential is fine — it's conditional and can't be included in the gather without making an unconditional API call.services/stats_service.py— Same false-sequential pattern, same fix. Thetry/except Exceptionwrapper around the gather correctly catches the first-exception propagation fromgather(return_exceptions=False)and returns(None, None). ✅ Theimport asyncioaddition is correct.Exception semantics — With
gather(return_exceptions=False)(the default), if one coroutine raises, the exception propagates immediately but other tasks are NOT cancelled — they continue running in the background. For the stats service, this means a batting API failure won't cancel the pitching request; the pitching result is simply discarded. This is acceptable behavior and matches the intent of the(None, None)fallback.Tests (
tests/test_services_stats.py)5 tests are well-structured and cover the key paths: both stats, batting-only, pitching-only, no stats, and exception handling. The
test_exception_returns_none_tupletest is correct —get_batting_statsraisesRuntimeError,asyncio.gatherpropagates it, the outertry/exceptcatches it, and(None, None)is returned. ✅Docstrings explain both what and why, per project conventions.
One minor note: there's no explicit test for the
mymovesgather change, but the existing 17/17 transaction command tests provide behavioral coverage.Cosmetic Formatting
The diff includes extensive linter reformatting of
management.py(single→double quotes, trailing commas, line length). All cosmetic, consistent with patterns from prior PRs (#77, #86, #100). No functional impact.No Issues Found
next-release✅@logged_commanddecorator handles exceptions from the gather inmymoves✅asynciowas already imported inmanagement.py(used by the pre-existing/legalgathers) ✅