fix(odds): use update_interval as cache TTL and fix live game cache refresh (#268)

* fix(odds): use 2-minute cache for live games instead of 30 minutes

Live game odds were being cached for 30 minutes because the cache key
didn't trigger the odds_live cache strategy. Added is_live parameter
to get_odds() and include 'live' suffix in cache key for live games,
which triggers the existing odds_live strategy (2 min TTL).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(base-odds): Use interval as TTL for cache operations

- Pass interval variable as TTL to cache_manager.set() calls
- Ensures cache expires after update interval, preventing stale data
- Removes dead code by actually using the computed interval value

* refactor(base-odds): Remove is_live parameter from base class for modularity

- Remove is_live parameter from get_odds() method signature
- Remove cache key modification logic from base class
- Remove is_live handling from get_odds_for_games()
- Keep base class minimal and generic for reuse by other plugins
- Plugin-specific is_live logic moved to odds-ticker plugin override

---------

Co-authored-by: Chuck <chuck@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-02-23 17:21:57 -05:00
committed by GitHub
parent 14b6a0c6a3
commit 4c4efd614a
2 changed files with 15 additions and 14 deletions

View File

@@ -415,7 +415,8 @@ class SportsCore(ABC):
sport=self.sport, sport=self.sport,
league=self.league, league=self.league,
event_id=game['id'], event_id=game['id'],
update_interval_seconds=update_interval update_interval_seconds=update_interval,
is_live=is_live
) )
if odds_data: if odds_data:

View File

@@ -84,7 +84,7 @@ class BaseOddsManager:
except Exception as e: except Exception as e:
self.logger.warning(f"Failed to load BaseOddsManager configuration: {e}") self.logger.warning(f"Failed to load BaseOddsManager configuration: {e}")
def get_odds(self, sport: str | None, league: str | None, event_id: str, def get_odds(self, sport: str | None, league: str | None, event_id: str,
update_interval_seconds: int = None) -> Optional[Dict[str, Any]]: update_interval_seconds: int = None) -> Optional[Dict[str, Any]]:
""" """
Fetch odds data for a specific game. Fetch odds data for a specific game.
@@ -94,13 +94,13 @@ class BaseOddsManager:
league: League name (e.g., 'nfl', 'nba') league: League name (e.g., 'nfl', 'nba')
event_id: ESPN event ID event_id: ESPN event ID
update_interval_seconds: Override default update interval update_interval_seconds: Override default update interval
Returns: Returns:
Dictionary containing odds data or None if unavailable Dictionary containing odds data or None if unavailable
""" """
if sport is None or league is None: if sport is None or league is None:
raise ValueError("Sport and League cannot be None") raise ValueError("Sport and League cannot be None")
# Use provided interval or default # Use provided interval or default
interval = update_interval_seconds or self.update_interval interval = update_interval_seconds or self.update_interval
cache_key = f"odds_espn_{sport}_{league}_{event_id}" cache_key = f"odds_espn_{sport}_{league}_{event_id}"
@@ -143,12 +143,12 @@ class BaseOddsManager:
self.logger.debug("No odds data available for this game") self.logger.debug("No odds data available for this game")
if odds_data: if odds_data:
self.cache_manager.set(cache_key, odds_data) self.cache_manager.set(cache_key, odds_data, ttl=interval)
self.logger.info(f"Saved odds data to cache for {cache_key}") self.logger.info(f"Saved odds data to cache for {cache_key} with TTL {interval}s")
else: else:
self.logger.debug(f"No odds data available for {cache_key}") self.logger.debug(f"No odds data available for {cache_key}")
# Cache the fact that no odds are available to avoid repeated API calls # Cache the fact that no odds are available to avoid repeated API calls
self.cache_manager.set(cache_key, {"no_odds": True}) self.cache_manager.set(cache_key, {"no_odds": True}, ttl=interval)
return odds_data return odds_data
@@ -208,34 +208,34 @@ class BaseOddsManager:
def get_odds_for_games(self, games: List[Dict[str, Any]]) -> List[Dict[str, Any]]: def get_odds_for_games(self, games: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
""" """
Fetch odds for multiple games efficiently. Fetch odds for multiple games efficiently.
Args: Args:
games: List of game dictionaries with sport, league, and id games: List of game dictionaries with sport, league, and id
Returns: Returns:
List of games with odds data added List of games with odds data added
""" """
games_with_odds = [] games_with_odds = []
for game in games: for game in games:
try: try:
sport = game.get('sport') sport = game.get('sport')
league = game.get('league') league = game.get('league')
event_id = game.get('id') event_id = game.get('id')
if sport and league and event_id: if sport and league and event_id:
odds_data = self.get_odds(sport, league, event_id) odds_data = self.get_odds(sport, league, event_id)
game['odds'] = odds_data game['odds'] = odds_data
else: else:
game['odds'] = None game['odds'] = None
games_with_odds.append(game) games_with_odds.append(game)
except Exception as e: except Exception as e:
self.logger.error(f"Error fetching odds for game {game.get('id', 'unknown')}: {e}") self.logger.error(f"Error fetching odds for game {game.get('id', 'unknown')}: {e}")
game['odds'] = None game['odds'] = None
games_with_odds.append(game) games_with_odds.append(game)
return games_with_odds return games_with_odds
def is_odds_available(self, odds_data: Optional[Dict[str, Any]]) -> bool: def is_odds_available(self, odds_data: Optional[Dict[str, Any]]) -> bool: