5.8 KiB
| title | description | type | domain | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Major Domo v2 Release — 2026.3.20 | Performance release: parallelized API calls, caching improvements, CI overhaul to tag-triggered releases, async hotfix, and chart path fix. | reference | major-domo |
|
Major Domo v2 Release — 2026.3.20
Date: 2026-03-20
Tags: 2026.3.10, 2026.3.11 (bugfix)
Image: manticorum67/major-domo-discordapp:production
Server: akamai (/root/container-data/major-domo)
Deploy method: .scripts/release.sh → CI → .scripts/deploy.sh
Release Summary
Performance-focused release with 12 merged PRs covering parallelized API calls, caching improvements, CI workflow overhaul, and a production hotfix. Also retired the next-release staging branch in favor of direct-to-main merges with tag-triggered releases.
Hotfix During Release
PR #117 — ScorecardTracker async mismatch. PR #106 added await to all scorecard_tracker method calls across scorebug.py, live_scorebug_tracker.py, and cleanup_service.py, but the tracker methods themselves were still synchronous. This caused TypeError: object NoneType can't be used in 'await' expression on /scorebug and TypeError: object list can't be used in 'await' expression in the background scorebug update loop. Fixed by making all 6 public ScorecardTracker methods async and adding 5 missing awaits in cleanup_service.py.
Root cause: PR #106 was created by an issue-worker agent that modified callers without modifying the tracker class. The async tracker conversion existed only in uncommitted working tree changes that were never included in any PR.
Lesson: Issue-worker agent PRs that add await to calls must verify the called methods are actually async — not just that the callers compile.
Infrastructure Changes
CI: Tag-triggered releases (PRs #110, #113)
Replaced branch-push CI with tag-push CI. Merging to main no longer triggers a Docker build.
- Before: Push to
mainornext-release→ auto-build → auto-tag CalVer - After: Push CalVer tag (
git tag 2026.3.11 && git push --tags) → build → Docker image tagged:version+:production
Also removed the pull_request trigger that was building Docker images on every PR branch push.
Release and deploy scripts (PRs #114, #115)
.scripts/release.sh— auto-generates next CalVer tag, shows changelog, confirms, pushes tag.scripts/deploy.sh— updated to use SSH alias (ssh akamai) and:productionimage tag
Docker volume split (PR #86)
Split the single ./storage:/app/data volume into:
./storage/major-domo-service-creds.json:/app/data/major-domo-service-creds.json:ro(credentials)./storage:/app/storage:rw(state files)
Production compose on akamai was updated manually before deploy. All 5 tracker default paths changed from data/ to storage/.
Retired next-release branch
All references to next-release removed from CLAUDE.md and CI workflow. New workflow: branch from main → PR to main → tag to release.
Performance Changes
Parallelized API calls
| PR | What | Impact |
|---|---|---|
| #88 | schedule_service: get_team_schedule, get_recent_games, get_upcoming_games use asyncio.gather() |
Up to 18 sequential HTTP requests → concurrent |
| #90 | Team lookups in /publish-scorecard, /scorebug, /injury, trade validation |
2 sequential calls → concurrent per location |
| #102 | asyncio.gather() across multiple command files |
Broad latency reduction |
Caching
| PR | What | Impact |
|---|---|---|
| #99 | Cache user team lookup in player_autocomplete with 60s TTL, reduce Discord limit to 25 |
Faster autocomplete on repeat use |
| #98 | Replace Redis KEYS with SCAN for cache invalidation |
Non-blocking invalidation |
Micro-optimizations
| PR | What | Impact |
|---|---|---|
| #93 | Use channel.purge() instead of per-message message.delete() loops |
1 API call vs up to 100 per channel clear |
| #96 | Replace json.dumps(value) probe with isinstance() in JSON logger |
Eliminates full serialization on every log call |
| #97 | Cache inspect.signature() at decoration time in all 3 decorators |
Introspection cost paid once, not per-call |
Cleanup
| PR | What |
|---|---|
| #104 | Remove dead @self.tree.interaction_check decorator block and duplicate self.maintenance_mode assignment in bot.py |
| #103 | Remove unused weeks_ahead parameter from get_upcoming_games |
Test Coverage
- 16 new tests for
schedule_service(test_services_schedule.py— first coverage for this service) - Tests use existing
GameFactory/TeamFactoryfromtests/factories.py - 2 existing scorebug tests updated for async tracker methods
- Full suite: 967+ tests passing
Bugfix: 2026.3.11
PR #119 — chart_service.py CHARTS_FILE path still pointed to data/charts.json after PR #86 moved state files to storage/. The /charts autocomplete returned no results because the file was at /app/storage/charts.json but the code read from /app/data/charts.json. One-line path fix.
Root cause: PR #86 updated the 5 tracker classes but missed ChartService, which uses a class-level Path constant instead of the __init__ pattern used by the trackers.
Lesson: When moving file paths across volumes, grep for the old path across the entire codebase — not just the files being modified.
Deployment Notes
- Production compose updated on akamai before deploy (volume split for PR #86)
- Image tag changed from
:latestto:production - Deployed three times total:
2026.3.10— initial release (broken/scorebugand scorebug tracker)2026.3.10— re-tagged after hotfix #117 (async mismatch)2026.3.11— chart path fix (#119)
- Final deploy confirmed healthy — all background tasks started, gateway connected