perf: add caching for frequently-accessed stable data #91

Open
opened 2026-03-20 13:17:27 +00:00 by cal · 1 comment
Owner

Problem

Several frequently-called service methods fetch data that rarely changes but have no caching.

league_service.get_current_state() — no cache

  • Called by transaction_builder.validate_transaction() on every validation render (line 515)
  • Called by transaction_service.get_pending/processed_transactions() on every invocation
  • The current week changes at most once per week. A 60-second TTL would eliminate most redundant calls.

standings_service.get_league_standings() — no cache

  • get_team_standings() and get_standings_by_division() both fetch full standings, then filter client-side
  • Standings change at most a few times per day after game processing
  • A 5–10 minute TTL would be appropriate

dice/rolls.py:359–373 — embed color lookup on every dice roll

  • _get_channel_embed_color() parses channel name for team abbreviation, then calls team_service.get_team_by_abbrev()
  • Falls back to get_teams_by_owner() — potentially 2 API calls per dice roll
  • /ab, /d20, /fielding, /jump are the highest-frequency commands
  • Team-channel associations change rarely — cache per channel ID with 5-minute TTL

player_service.get_top_free_agents() — no cache

  • Fetches full free agent list, sorts client-side
  • Free agents change infrequently — short TTL cache appropriate

Fix

Add @cached_api_call(ttl=...) or @cached_single_item(ttl=...) decorators where appropriate. For the embed color lookup, a simple in-memory dict cache keyed by channel ID with TTL is sufficient.

Impact

MEDIUM-HIGH — The dice roll color lookup affects the most frequently used commands. The league state caching affects transaction workflows.

## Problem Several frequently-called service methods fetch data that rarely changes but have no caching. ### `league_service.get_current_state()` — no cache - Called by `transaction_builder.validate_transaction()` on every validation render (line 515) - Called by `transaction_service.get_pending/processed_transactions()` on every invocation - The current week changes at most once per week. A 60-second TTL would eliminate most redundant calls. ### `standings_service.get_league_standings()` — no cache - `get_team_standings()` and `get_standings_by_division()` both fetch full standings, then filter client-side - Standings change at most a few times per day after game processing - A 5–10 minute TTL would be appropriate ### `dice/rolls.py:359–373` — embed color lookup on every dice roll - `_get_channel_embed_color()` parses channel name for team abbreviation, then calls `team_service.get_team_by_abbrev()` - Falls back to `get_teams_by_owner()` — potentially 2 API calls per dice roll - `/ab`, `/d20`, `/fielding`, `/jump` are the highest-frequency commands - Team-channel associations change rarely — cache per channel ID with 5-minute TTL ### `player_service.get_top_free_agents()` — no cache - Fetches full free agent list, sorts client-side - Free agents change infrequently — short TTL cache appropriate ## Fix Add `@cached_api_call(ttl=...)` or `@cached_single_item(ttl=...)` decorators where appropriate. For the embed color lookup, a simple in-memory dict cache keyed by channel ID with TTL is sufficient. ## Impact **MEDIUM-HIGH** — The dice roll color lookup affects the most frequently used commands. The league state caching affects transaction workflows.
cal added the
ai-working
label 2026-03-20 13:18:37 +00:00
cal removed the
ai-working
label 2026-03-20 13:21:32 +00:00
Claude added the
ai-working
label 2026-03-21 12:31:51 +00:00
Claude added the
status/in-progress
label 2026-03-21 12:33:37 +00:00
Claude removed the
status/in-progress
label 2026-03-21 12:36:07 +00:00
Collaborator

PR #120 opened: #120

Added caching to all four locations identified in the issue:

  • league_service.get_current_state()@cached_single_item(ttl=60) (Redis)
  • standings_service.get_league_standings() → in-memory dict cache, 10-minute TTL (StandingsService doesn't extend BaseService so used the autocomplete.py pattern)
  • player_service.get_free_agents()@cached_api_call(ttl=300) (Redis, benefits get_top_free_agents too)
  • dice/rolls.py _get_channel_embed_color() → in-memory dict cache keyed by channel_id, 5-minute TTL
PR #120 opened: https://git.manticorum.com/cal/major-domo-v2/pulls/120 Added caching to all four locations identified in the issue: - `league_service.get_current_state()` → `@cached_single_item(ttl=60)` (Redis) - `standings_service.get_league_standings()` → in-memory dict cache, 10-minute TTL (StandingsService doesn't extend BaseService so used the autocomplete.py pattern) - `player_service.get_free_agents()` → `@cached_api_call(ttl=300)` (Redis, benefits get_top_free_agents too) - `dice/rolls.py _get_channel_embed_color()` → in-memory dict cache keyed by channel_id, 5-minute TTL
Claude added
status/pr-open
ai-pr-opened
and removed
ai-working
labels 2026-03-21 12:36:21 +00:00
Sign in to join this conversation.
No Milestone
No project
No Assignees
2 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#91
No description provided.