Merge next-release into main #111

Merged
cal merged 24 commits from next-release into main 2026-03-20 17:54:29 +00:00
Owner

Summary

Performance-focused release with 11 merged PRs: parallelized API calls, caching improvements, CI cleanup, and a Docker volume fix.

Changes

Performance

  • #88 — Parallelize schedule_service week fetches with asyncio.gather()
  • #90 — Parallelize independent API calls (team lookups, injury commands, trade validation)
  • #93 — Use channel.purge() instead of per-message delete loops
  • #96 — Replace json.dumps serialization test with isinstance fast path in logger
  • #97 — Cache inspect.signature() at decoration time instead of per-call
  • #98 — Replace Redis KEYS with SCAN for cache invalidation
  • #99 — Cache user team lookup in player_autocomplete, reduce limit to 25
  • #102 — Replace sequential awaits with asyncio.gather() across commands

Fixes

  • #85 — Split read-only data volume to allow state file writes

Cleanup / CI

  • #104 — Remove dead maintenance mode artifacts in bot.py
  • #110 — Remove pull_request trigger from Docker build workflow (only build on merge)

Deployment notes

⚠️ Docker compose updated on akamai — volume mounts changed for PR #85:

  • Credentials file-mounted read-only: ./storage/major-domo-service-creds.json:/app/data/major-domo-service-creds.json:ro
  • State files writable: ./storage:/app/storage:rw
  • Compose already updated on production host, will take effect on next container restart
## Summary Performance-focused release with 11 merged PRs: parallelized API calls, caching improvements, CI cleanup, and a Docker volume fix. ## Changes ### Performance - **#88** — Parallelize schedule_service week fetches with `asyncio.gather()` - **#90** — Parallelize independent API calls (team lookups, injury commands, trade validation) - **#93** — Use `channel.purge()` instead of per-message delete loops - **#96** — Replace `json.dumps` serialization test with `isinstance` fast path in logger - **#97** — Cache `inspect.signature()` at decoration time instead of per-call - **#98** — Replace Redis `KEYS` with `SCAN` for cache invalidation - **#99** — Cache user team lookup in `player_autocomplete`, reduce limit to 25 - **#102** — Replace sequential awaits with `asyncio.gather()` across commands ### Fixes - **#85** — Split read-only data volume to allow state file writes ### Cleanup / CI - **#104** — Remove dead maintenance mode artifacts in bot.py - **#110** — Remove `pull_request` trigger from Docker build workflow (only build on merge) ## Deployment notes ⚠️ **Docker compose updated on akamai** — volume mounts changed for PR #85: - Credentials file-mounted read-only: `./storage/major-domo-service-creds.json:/app/data/major-domo-service-creds.json:ro` - State files writable: `./storage:/app/storage:rw` - Compose already updated on production host, will take effect on next container restart
cal added 24 commits 2026-03-20 17:54:06 +00:00
fix: split read-only data volume to allow state file writes (#85)
All checks were successful
Build Docker Image / build (pull_request) Successful in 57s
03dd449551
The data/ volume was mounted :ro to protect Google Sheets credentials,
but this also prevented all state trackers from persisting JSON files
(scorecards, voice channels, trade channels, soak data), causing silent
save failures and stale data accumulating across restarts.

- Mount only the credentials file as :ro (file-level mount)
- Add a separate :rw storage/ volume for runtime state files
- Move all tracker default paths from data/ to storage/
- Add STATE_HOST_PATH env var (defaults to ./storage)
- Update SHEETS_CREDENTIALS_HOST_PATH semantics: now a file path
  (e.g. ./data/major-domo-service-creds.json) instead of a directory
- Add storage/ to .gitignore

Closes #85

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
perf: cache user team lookup in player_autocomplete, reduce limit to 25
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m16s
c8ed4dee38
Closes #99

- Add module-level `_user_team_cache` dict with 60-second TTL so
  `get_user_major_league_team` is called at most once per minute per
  user instead of on every keystroke.
- Reduce `search_players(limit=50)` to `limit=25` to match Discord's
  25-choice display cap and avoid fetching unused results.
- Add `TestGetCachedUserTeam` class covering cache hit, TTL expiry, and
  None caching; add `clear_user_team_cache` autouse fixture to prevent
  test interference via module-level state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
perf: replace Redis KEYS with SCAN in clear_prefix (#98)
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m22s
df9e9bedbe
Closes #98

Replace blocking `client.keys(pattern)` with non-blocking
`client.scan_iter(match=pattern)` to avoid full-keyspace scans
that block the Redis server during cache invalidation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes #87

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reviewed-on: #102
perf: parallelize schedule_service week fetches with asyncio.gather (#88)
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m20s
b480120731
Closes #88

Replaced sequential for-loops in get_team_schedule(), get_recent_games(),
and get_upcoming_games() with asyncio.gather() to fire all per-week HTTP
requests concurrently. Also adds import asyncio which was missing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
refactor: use GameFactory/TeamFactory in schedule service tests
All checks were successful
Build Docker Image / build (pull_request) Successful in 58s
0992acf718
Replace custom _make_game/_make_team helpers with existing test
factories for consistency with the rest of the test suite.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The parameter was already ignored (body hardcodes range(1, 19)).
Remove from signature and the one caller that passed it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reviewed-on: #103
Reviewed-on: #101
Reviewed-on: #100
Reviewed-on: #86
cleanup: remove dead maintenance mode artifacts in bot.py (#104)
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m6s
008d6be86c
Closes #104

- Remove duplicate `self.maintenance_mode: bool = False` assignment (merge
  artifact from PR #83)
- Delete dead `@self.tree.interaction_check` block in `setup_hook` that
  generated a RuntimeWarning at startup; `MaintenanceAwareTree.interaction_check()`
  already handles this correctly via the `tree_cls=MaintenanceAwareTree` kwarg

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
perf: use channel.purge() instead of per-message delete loops (#93)
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m16s
8878ce85f7
Closes #93

Replace sequential message.delete() loops with channel.purge() bulk
delete in three locations:
- commands/admin/management.py: admin_clear_scorecards (up to 100 msgs)
- tasks/live_scorebug_tracker.py: _post_scorebugs_to_channel (25 msgs)
- tasks/live_scorebug_tracker.py: _clear_live_scores_channel (25 msgs)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
perf: replace json.dumps serialization test with isinstance fast path (#96)
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m22s
70c4555a74
Closes #96

Replaces the per-field `json.dumps(value)` probe — which fully serialized
and discarded the result just to check serializability — with a type-check
fast path using `isinstance()`. The `_SERIALIZABLE_TYPES` tuple is defined
at module level so it's not recreated on every log call.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reviewed-on: #109
The PR trigger caused Docker builds on every push to PR branches,
wasting CI resources. Only build on merge to main/next-release.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reviewed-on: #110
Reviewed-on: #108
Move inspect.signature(func) calls from inside wrapper functions to the
outer decorator function so the introspection cost is paid once at
decoration time instead of on every invocation.

- logged_command: sig, param_names, and exclude_set computed at decoration time;
  wrapper.__signature__ reuses the pre-computed sig
- cached_api_call: sig moved to decorator scope; bound_args still computed
  per-call (requires runtime args)
- cached_single_item: same as cached_api_call

Closes #97

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reviewed-on: #107
Closes #90

Replace sequential awaits with asyncio.gather() in all locations identified
in the issue:

- commands/gameplay/scorebug.py: parallel team lookups in publish_scorecard
  and scorebug commands; also fix missing await on async scorecard_tracker calls
- commands/league/submit_scorecard.py: parallel away/home team lookups
- tasks/live_scorebug_tracker.py: parallel team lookups inside per-scorecard
  loop (compounds across multiple active games); fix missing await on
  get_all_scorecards
- commands/injuries/management.py: parallel get_current_state() +
  search_players() in injury_roll, injury_set_new, and injury_clear
- services/trade_builder.py: parallel per-participant roster validation in
  validate_trade()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reviewed-on: #106
Reviewed-on: #105
cal merged commit fd24a41422 into main 2026-03-20 17:54:29 +00:00
cal deleted branch next-release 2026-03-20 17:54:30 +00:00
Sign in to join this conversation.
No reviewers
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: cal/major-domo-v2#111
No description provided.