mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-10 21:03:01 +00:00
Feature/memory optimization config (#108)
* feat(config): optimize memory usage to prevent OOM killer - Reduce brightness from 95 to 50 to lower power consumption - Reduce refresh rate from 120Hz to 100Hz to reduce CPU/memory pressure - Reduce background service workers from 3 to 1 per manager - Change hardware mapping from adafruit-hat-pwm to adafruit-hat - Expected memory savings: ~700MB reduction in background service usage - Addresses SIGKILL errors caused by memory exhaustion on Raspberry Pi Fixes: OOM killer terminating ledmatrix.service with status=9/KILL * revert display brightness * refactor(background-service): hardcode optimized settings and remove config blocks - Hardcode background service to 1 worker in all managers - Remove background_service config blocks from template - Simplify configuration for users - no need to adjust system settings - Memory optimization: ~700MB reduction in background service usage - Settings: 1 worker, 30s timeout, 3 retries (hardcoded) Files updated: - src/base_classes/sports.py - src/leaderboard_manager.py - src/odds_ticker_manager.py - src/soccer_managers.py - src/milb_manager.py - config/config.template.json This prevents OOM killer errors by reducing memory usage from 15 background threads to 5 threads total across all managers. * remove fallback in case of background service failure
This commit is contained in:
Submodule LEDMatrix.wiki updated: a01c72e156...fbd8d89a18
@@ -17,8 +17,8 @@
|
|||||||
"cols": 64,
|
"cols": 64,
|
||||||
"chain_length": 2,
|
"chain_length": 2,
|
||||||
"parallel": 1,
|
"parallel": 1,
|
||||||
"brightness": 95,
|
"brightness": 90,
|
||||||
"hardware_mapping": "adafruit-hat-pwm",
|
"hardware_mapping": "adafruit-hat",
|
||||||
"scan_mode": 0,
|
"scan_mode": 0,
|
||||||
"pwm_bits": 9,
|
"pwm_bits": 9,
|
||||||
"pwm_dither_bits": 1,
|
"pwm_dither_bits": 1,
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
"disable_hardware_pulsing": false,
|
"disable_hardware_pulsing": false,
|
||||||
"inverse_colors": false,
|
"inverse_colors": false,
|
||||||
"show_refresh_rate": false,
|
"show_refresh_rate": false,
|
||||||
"limit_refresh_rate_hz": 120
|
"limit_refresh_rate_hz": 100
|
||||||
},
|
},
|
||||||
"runtime": {
|
"runtime": {
|
||||||
"gpio_slowdown": 3
|
"gpio_slowdown": 3
|
||||||
@@ -152,14 +152,7 @@
|
|||||||
"dynamic_duration": true,
|
"dynamic_duration": true,
|
||||||
"min_duration": 30,
|
"min_duration": 30,
|
||||||
"max_duration": 300,
|
"max_duration": 300,
|
||||||
"duration_buffer": 0.1,
|
"duration_buffer": 0.1
|
||||||
"background_service": {
|
|
||||||
"enabled": true,
|
|
||||||
"max_workers": 3,
|
|
||||||
"request_timeout": 30,
|
|
||||||
"max_retries": 3,
|
|
||||||
"priority": 2
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"leaderboard": {
|
"leaderboard": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
@@ -202,14 +195,7 @@
|
|||||||
"request_timeout": 30,
|
"request_timeout": 30,
|
||||||
"dynamic_duration": true,
|
"dynamic_duration": true,
|
||||||
"min_duration": 30,
|
"min_duration": 30,
|
||||||
"max_display_time": 600,
|
"max_display_time": 600
|
||||||
"background_service": {
|
|
||||||
"enabled": true,
|
|
||||||
"max_workers": 3,
|
|
||||||
"request_timeout": 30,
|
|
||||||
"max_retries": 3,
|
|
||||||
"priority": 2
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"calendar": {
|
"calendar": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
@@ -241,13 +227,6 @@
|
|||||||
],
|
],
|
||||||
"logo_dir": "assets/sports/nhl_logos",
|
"logo_dir": "assets/sports/nhl_logos",
|
||||||
"show_records": true,
|
"show_records": true,
|
||||||
"background_service": {
|
|
||||||
"enabled": true,
|
|
||||||
"max_workers": 3,
|
|
||||||
"request_timeout": 30,
|
|
||||||
"max_retries": 3,
|
|
||||||
"priority": 2
|
|
||||||
},
|
|
||||||
"display_modes": {
|
"display_modes": {
|
||||||
"nhl_live": true,
|
"nhl_live": true,
|
||||||
"nhl_recent": true,
|
"nhl_recent": true,
|
||||||
@@ -275,13 +254,6 @@
|
|||||||
],
|
],
|
||||||
"logo_dir": "assets/sports/nba_logos",
|
"logo_dir": "assets/sports/nba_logos",
|
||||||
"show_records": true,
|
"show_records": true,
|
||||||
"background_service": {
|
|
||||||
"enabled": true,
|
|
||||||
"max_workers": 3,
|
|
||||||
"request_timeout": 30,
|
|
||||||
"max_retries": 3,
|
|
||||||
"priority": 2
|
|
||||||
},
|
|
||||||
"display_modes": {
|
"display_modes": {
|
||||||
"nba_live": true,
|
"nba_live": true,
|
||||||
"nba_recent": true,
|
"nba_recent": true,
|
||||||
@@ -309,13 +281,6 @@
|
|||||||
],
|
],
|
||||||
"logo_dir": "assets/sports/wnba_logos",
|
"logo_dir": "assets/sports/wnba_logos",
|
||||||
"show_records": true,
|
"show_records": true,
|
||||||
"background_service": {
|
|
||||||
"enabled": true,
|
|
||||||
"max_workers": 3,
|
|
||||||
"request_timeout": 30,
|
|
||||||
"max_retries": 3,
|
|
||||||
"priority": 2
|
|
||||||
},
|
|
||||||
"display_modes": {
|
"display_modes": {
|
||||||
"wnba_live": true,
|
"wnba_live": true,
|
||||||
"wnba_recent": true,
|
"wnba_recent": true,
|
||||||
@@ -344,13 +309,6 @@
|
|||||||
],
|
],
|
||||||
"logo_dir": "assets/sports/nfl_logos",
|
"logo_dir": "assets/sports/nfl_logos",
|
||||||
"show_records": true,
|
"show_records": true,
|
||||||
"background_service": {
|
|
||||||
"enabled": true,
|
|
||||||
"max_workers": 3,
|
|
||||||
"request_timeout": 30,
|
|
||||||
"max_retries": 3,
|
|
||||||
"priority": 2
|
|
||||||
},
|
|
||||||
"display_modes": {
|
"display_modes": {
|
||||||
"nfl_live": true,
|
"nfl_live": true,
|
||||||
"nfl_recent": true,
|
"nfl_recent": true,
|
||||||
@@ -378,13 +336,6 @@
|
|||||||
"logo_dir": "assets/sports/ncaa_logos",
|
"logo_dir": "assets/sports/ncaa_logos",
|
||||||
"show_records": true,
|
"show_records": true,
|
||||||
"show_ranking": true,
|
"show_ranking": true,
|
||||||
"background_service": {
|
|
||||||
"enabled": true,
|
|
||||||
"max_workers": 3,
|
|
||||||
"request_timeout": 30,
|
|
||||||
"max_retries": 3,
|
|
||||||
"priority": 2
|
|
||||||
},
|
|
||||||
"display_modes": {
|
"display_modes": {
|
||||||
"ncaa_fb_live": true,
|
"ncaa_fb_live": true,
|
||||||
"ncaa_fb_recent": true,
|
"ncaa_fb_recent": true,
|
||||||
@@ -570,13 +521,6 @@
|
|||||||
"logo_dir": "assets/sports/milb_logos",
|
"logo_dir": "assets/sports/milb_logos",
|
||||||
"show_records": true,
|
"show_records": true,
|
||||||
"upcoming_fetch_days": 7,
|
"upcoming_fetch_days": 7,
|
||||||
"background_service": {
|
|
||||||
"enabled": true,
|
|
||||||
"max_workers": 3,
|
|
||||||
"request_timeout": 30,
|
|
||||||
"max_retries": 3,
|
|
||||||
"priority": 2
|
|
||||||
},
|
|
||||||
"display_modes": {
|
"display_modes": {
|
||||||
"milb_live": true,
|
"milb_live": true,
|
||||||
"milb_recent": true,
|
"milb_recent": true,
|
||||||
@@ -623,13 +567,6 @@
|
|||||||
],
|
],
|
||||||
"logo_dir": "assets/sports/soccer_logos",
|
"logo_dir": "assets/sports/soccer_logos",
|
||||||
"show_records": true,
|
"show_records": true,
|
||||||
"background_service": {
|
|
||||||
"enabled": true,
|
|
||||||
"max_workers": 3,
|
|
||||||
"request_timeout": 30,
|
|
||||||
"max_retries": 3,
|
|
||||||
"priority": 2
|
|
||||||
},
|
|
||||||
"display_modes": {
|
"display_modes": {
|
||||||
"soccer_live": true,
|
"soccer_live": true,
|
||||||
"soccer_recent": true,
|
"soccer_recent": true,
|
||||||
|
|||||||
@@ -105,19 +105,12 @@ class SportsCore(ABC):
|
|||||||
self._rankings_cache_timestamp = 0
|
self._rankings_cache_timestamp = 0
|
||||||
self._rankings_cache_duration = 3600 # Cache rankings for 1 hour
|
self._rankings_cache_duration = 3600 # Cache rankings for 1 hour
|
||||||
|
|
||||||
# Initialize background data service
|
# Initialize background data service with optimized settings
|
||||||
background_config = self.mode_config.get("background_service", {})
|
# Hardcoded for memory optimization: 1 worker, 30s timeout, 3 retries
|
||||||
if background_config.get("enabled", True): # Default to enabled
|
self.background_service = get_background_service(self.cache_manager, max_workers=1)
|
||||||
max_workers = background_config.get("max_workers", 3)
|
|
||||||
self.background_service = get_background_service(self.cache_manager, max_workers)
|
|
||||||
self.background_fetch_requests = {} # Track background fetch requests
|
self.background_fetch_requests = {} # Track background fetch requests
|
||||||
self.background_enabled = True
|
self.background_enabled = True
|
||||||
self.logger.info(f"Background service enabled with {max_workers} workers")
|
self.logger.info("Background service enabled with 1 worker (memory optimized)")
|
||||||
else:
|
|
||||||
self.background_service = None
|
|
||||||
self.background_fetch_requests = {}
|
|
||||||
self.background_enabled = False
|
|
||||||
self.logger.info("Background service disabled")
|
|
||||||
|
|
||||||
def _get_season_schedule_dates(self) -> tuple[str, str]:
|
def _get_season_schedule_dates(self) -> tuple[str, str]:
|
||||||
return "", ""
|
return "", ""
|
||||||
|
|||||||
@@ -71,19 +71,12 @@ class LeaderboardManager:
|
|||||||
# Store reference to config instead of creating new ConfigManager
|
# Store reference to config instead of creating new ConfigManager
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
# Initialize background data service
|
# Initialize background data service with optimized settings
|
||||||
background_config = self.leaderboard_config.get("background_service", {})
|
# Hardcoded for memory optimization: 1 worker, 30s timeout, 3 retries
|
||||||
if background_config.get("enabled", True): # Default to enabled
|
self.background_service = get_background_service(self.cache_manager, max_workers=1)
|
||||||
max_workers = background_config.get("max_workers", 3)
|
|
||||||
self.background_service = get_background_service(self.cache_manager, max_workers)
|
|
||||||
self.background_fetch_requests = {} # Track background fetch requests
|
self.background_fetch_requests = {} # Track background fetch requests
|
||||||
self.background_enabled = True
|
self.background_enabled = True
|
||||||
logger.info(f"[Leaderboard] Background service enabled with {max_workers} workers")
|
logger.info("[Leaderboard] Background service enabled with 1 worker (memory optimized)")
|
||||||
else:
|
|
||||||
self.background_service = None
|
|
||||||
self.background_fetch_requests = {}
|
|
||||||
self.background_enabled = False
|
|
||||||
logger.info("[Leaderboard] Background service disabled")
|
|
||||||
|
|
||||||
# State variables
|
# State variables
|
||||||
self.last_update = 0
|
self.last_update = 0
|
||||||
|
|||||||
@@ -75,19 +75,12 @@ class BaseMiLBManager(Baseball):
|
|||||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
|
||||||
}
|
}
|
||||||
|
|
||||||
# Initialize background data service
|
# Initialize background data service with optimized settings
|
||||||
background_config = self.milb_config.get("background_service", {})
|
# Hardcoded for memory optimization: 1 worker, 30s timeout, 3 retries
|
||||||
if background_config.get("enabled", True): # Default to enabled
|
self.background_service = get_background_service(self.cache_manager, max_workers=1)
|
||||||
max_workers = background_config.get("max_workers", 3)
|
|
||||||
self.background_service = get_background_service(self.cache_manager, max_workers)
|
|
||||||
self.background_fetch_requests = {} # Track background fetch requests
|
self.background_fetch_requests = {} # Track background fetch requests
|
||||||
self.background_enabled = True
|
self.background_enabled = True
|
||||||
self.logger.info(f"[MiLB] Background service enabled with {max_workers} workers")
|
self.logger.info("[MiLB] Background service enabled with 1 worker (memory optimized)")
|
||||||
else:
|
|
||||||
self.background_service = None
|
|
||||||
self.background_fetch_requests = {}
|
|
||||||
self.background_enabled = False
|
|
||||||
self.logger.info("[MiLB] Background service disabled")
|
|
||||||
|
|
||||||
def _probe_and_update_from_live_feed(self, game_pk: str, game_data: Dict[str, Any]) -> bool:
|
def _probe_and_update_from_live_feed(self, game_pk: str, game_data: Dict[str, Any]) -> bool:
|
||||||
"""Probe MLB Stats live feed for a game and update game_data in-place if live.
|
"""Probe MLB Stats live feed for a game and update game_data in-place if live.
|
||||||
|
|||||||
@@ -82,11 +82,6 @@ class BaseMLBManager(Baseball):
|
|||||||
# Clear invalid cache
|
# Clear invalid cache
|
||||||
self.cache_manager.clear_cache(cache_key)
|
self.cache_manager.clear_cache(cache_key)
|
||||||
|
|
||||||
# If background service is disabled, fall back to synchronous fetch
|
|
||||||
if not self.background_enabled or not self.background_service:
|
|
||||||
pass
|
|
||||||
# return self._fetch_ncaa_api_data_sync(use_cache)
|
|
||||||
|
|
||||||
self.logger.info(f"Fetching full {datestring} season schedule from ESPN API...")
|
self.logger.info(f"Fetching full {datestring} season schedule from ESPN API...")
|
||||||
|
|
||||||
# Start background fetch
|
# Start background fetch
|
||||||
|
|||||||
@@ -76,10 +76,6 @@ class BaseNBAManager(Basketball):
|
|||||||
# Clear invalid cache
|
# Clear invalid cache
|
||||||
self.cache_manager.clear_cache(cache_key)
|
self.cache_manager.clear_cache(cache_key)
|
||||||
|
|
||||||
# If background service is disabled, fall back to synchronous fetch
|
|
||||||
if not self.background_enabled or not self.background_service:
|
|
||||||
return self._fetch_nba_api_data_sync(use_cache)
|
|
||||||
|
|
||||||
# Start background fetch
|
# Start background fetch
|
||||||
self.logger.info(f"Starting background fetch for {season_year} season schedule...")
|
self.logger.info(f"Starting background fetch for {season_year} season schedule...")
|
||||||
|
|
||||||
@@ -124,30 +120,6 @@ class BaseNBAManager(Basketball):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _fetch_nba_api_data_sync(self, use_cache: bool = True) -> Optional[Dict]:
|
|
||||||
"""
|
|
||||||
Synchronous fallback for fetching NFL data when background service is disabled.
|
|
||||||
"""
|
|
||||||
now = datetime.now(pytz.utc)
|
|
||||||
current_year = now.year
|
|
||||||
cache_key = f"nba_schedule_{current_year}"
|
|
||||||
|
|
||||||
self.logger.info(f"Fetching full {current_year} season schedule from ESPN API (sync mode)...")
|
|
||||||
try:
|
|
||||||
response = self.session.get(ESPN_NBA_SCOREBOARD_URL, params={"dates": current_year, "limit":1000}, headers=self.headers, timeout=15)
|
|
||||||
response.raise_for_status()
|
|
||||||
data = response.json()
|
|
||||||
events = data.get('events', [])
|
|
||||||
|
|
||||||
if use_cache:
|
|
||||||
self.cache_manager.set(cache_key, events)
|
|
||||||
|
|
||||||
self.logger.info(f"Successfully fetched {len(events)} events for the {current_year} season.")
|
|
||||||
return {'events': events}
|
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
self.logger.error(f"API error fetching full schedule: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _fetch_data(self) -> Optional[Dict]:
|
def _fetch_data(self) -> Optional[Dict]:
|
||||||
"""Fetch data using shared data mechanism or direct fetch for live."""
|
"""Fetch data using shared data mechanism or direct fetch for live."""
|
||||||
if isinstance(self, NBALiveManager):
|
if isinstance(self, NBALiveManager):
|
||||||
|
|||||||
@@ -60,11 +60,6 @@ class BaseNCAABaseballManager(Baseball):
|
|||||||
# Clear invalid cache
|
# Clear invalid cache
|
||||||
self.cache_manager.clear_cache(cache_key)
|
self.cache_manager.clear_cache(cache_key)
|
||||||
|
|
||||||
# If background service is disabled, fall back to synchronous fetch
|
|
||||||
if not self.background_enabled or not self.background_service:
|
|
||||||
pass
|
|
||||||
# return self._fetch_ncaa_api_data_sync(use_cache)
|
|
||||||
|
|
||||||
self.logger.info(f"Fetching full {datestring} season schedule from ESPN API...")
|
self.logger.info(f"Fetching full {datestring} season schedule from ESPN API...")
|
||||||
|
|
||||||
# Start background fetch
|
# Start background fetch
|
||||||
|
|||||||
@@ -73,10 +73,6 @@ class BaseNCAAFBManager(Football): # Renamed class
|
|||||||
# Clear invalid cache
|
# Clear invalid cache
|
||||||
self.cache_manager.clear_cache(cache_key)
|
self.cache_manager.clear_cache(cache_key)
|
||||||
|
|
||||||
# If background service is disabled, fall back to synchronous fetch
|
|
||||||
if not self.background_enabled or not self.background_service:
|
|
||||||
return self._fetch_ncaa_api_data_sync(use_cache)
|
|
||||||
|
|
||||||
self.logger.info(f"Fetching full {season_year} season schedule from ESPN API...")
|
self.logger.info(f"Fetching full {season_year} season schedule from ESPN API...")
|
||||||
|
|
||||||
# Start background fetch
|
# Start background fetch
|
||||||
@@ -122,30 +118,6 @@ class BaseNCAAFBManager(Football): # Renamed class
|
|||||||
return partial_data
|
return partial_data
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _fetch_ncaa_api_data_sync(self, use_cache: bool = True) -> Optional[Dict]:
|
|
||||||
"""
|
|
||||||
Synchronous fallback for fetching NFL data when background service is disabled.
|
|
||||||
"""
|
|
||||||
now = datetime.now(pytz.utc)
|
|
||||||
current_year = now.year
|
|
||||||
cache_key = f"ncaafb_schedule_{current_year}"
|
|
||||||
|
|
||||||
self.logger.info(f"Fetching full {current_year} season schedule from ESPN API (sync mode)...")
|
|
||||||
try:
|
|
||||||
response = self.session.get(ESPN_NCAAFB_SCOREBOARD_URL, params={"dates": current_year, "limit":1000}, headers=self.headers, timeout=15)
|
|
||||||
response.raise_for_status()
|
|
||||||
data = response.json()
|
|
||||||
events = data.get('events', [])
|
|
||||||
|
|
||||||
if use_cache:
|
|
||||||
self.cache_manager.set(cache_key, events)
|
|
||||||
|
|
||||||
self.logger.info(f"Successfully fetched {len(events)} events for the {current_year} season.")
|
|
||||||
return {'events': events}
|
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
self.logger.error(f"[API error fetching full schedule: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _fetch_data(self) -> Optional[Dict]:
|
def _fetch_data(self) -> Optional[Dict]:
|
||||||
"""Fetch data using shared data mechanism or direct fetch for live."""
|
"""Fetch data using shared data mechanism or direct fetch for live."""
|
||||||
if isinstance(self, NCAAFBLiveManager):
|
if isinstance(self, NCAAFBLiveManager):
|
||||||
|
|||||||
@@ -97,10 +97,6 @@ class BaseNCAAMBasketballManager(Basketball):
|
|||||||
# Clear invalid cache
|
# Clear invalid cache
|
||||||
self.cache_manager.clear_cache(cache_key)
|
self.cache_manager.clear_cache(cache_key)
|
||||||
|
|
||||||
# If background service is disabled, fall back to synchronous fetch
|
|
||||||
if not self.background_enabled or not self.background_service:
|
|
||||||
return self._fetch_ncaam_basketball_api_data_sync(use_cache)
|
|
||||||
|
|
||||||
# Start background fetch
|
# Start background fetch
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"Starting background fetch for {season_year} season schedule..."
|
f"Starting background fetch for {season_year} season schedule..."
|
||||||
@@ -151,41 +147,6 @@ class BaseNCAAMBasketballManager(Basketball):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _fetch_ncaam_basketball_api_data_sync(
|
|
||||||
self, use_cache: bool = True
|
|
||||||
) -> Optional[Dict]:
|
|
||||||
"""
|
|
||||||
Synchronous fallback for fetching NFL data when background service is disabled.
|
|
||||||
"""
|
|
||||||
now = datetime.now(pytz.utc)
|
|
||||||
current_year = now.year
|
|
||||||
cache_key = f"ncaa_mens_basketball_schedule_{current_year}"
|
|
||||||
|
|
||||||
self.logger.info(
|
|
||||||
f"Fetching full {current_year} season schedule from ESPN API (sync mode)..."
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
response = self.session.get(
|
|
||||||
ESPN_NCAAMB_SCOREBOARD_URL,
|
|
||||||
params={"dates": current_year, "limit": 1000},
|
|
||||||
headers=self.headers,
|
|
||||||
timeout=15,
|
|
||||||
)
|
|
||||||
response.raise_for_status()
|
|
||||||
data = response.json()
|
|
||||||
events = data.get("events", [])
|
|
||||||
|
|
||||||
if use_cache:
|
|
||||||
self.cache_manager.set(cache_key, events)
|
|
||||||
|
|
||||||
self.logger.info(
|
|
||||||
f"Successfully fetched {len(events)} events for the {current_year} season."
|
|
||||||
)
|
|
||||||
return {"events": events}
|
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
self.logger.error(f"API error fetching full schedule: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _fetch_data(self) -> Optional[Dict]:
|
def _fetch_data(self) -> Optional[Dict]:
|
||||||
"""Fetch data using shared data mechanism or direct fetch for live."""
|
"""Fetch data using shared data mechanism or direct fetch for live."""
|
||||||
if isinstance(self, NCAAMBasketballLiveManager):
|
if isinstance(self, NCAAMBasketballLiveManager):
|
||||||
|
|||||||
@@ -77,10 +77,6 @@ class BaseNCAAMHockeyManager(Hockey): # Renamed class
|
|||||||
# Clear invalid cache
|
# Clear invalid cache
|
||||||
self.cache_manager.clear_cache(cache_key)
|
self.cache_manager.clear_cache(cache_key)
|
||||||
|
|
||||||
# If background service is disabled, fall back to synchronous fetch
|
|
||||||
if not self.background_enabled or not self.background_service:
|
|
||||||
return self._fetch_ncaa_api_data_sync(use_cache)
|
|
||||||
|
|
||||||
self.logger.info(f"Fetching full {season_year} season schedule from ESPN API...")
|
self.logger.info(f"Fetching full {season_year} season schedule from ESPN API...")
|
||||||
|
|
||||||
# Start background fetch
|
# Start background fetch
|
||||||
@@ -126,31 +122,6 @@ class BaseNCAAMHockeyManager(Hockey): # Renamed class
|
|||||||
return partial_data
|
return partial_data
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _fetch_ncaa_api_data_sync(self, use_cache: bool = True) -> Optional[Dict]:
|
|
||||||
"""
|
|
||||||
Synchronous fallback for fetching NCAA Mens Hockey data when background service is disabled.
|
|
||||||
"""
|
|
||||||
now = datetime.now(pytz.utc)
|
|
||||||
current_year = now.year
|
|
||||||
cache_key = f"ncaa_mens_hockey_schedule_{current_year}"
|
|
||||||
|
|
||||||
self.logger.info(f"Fetching full {current_year} season schedule from ESPN API (sync mode)...")
|
|
||||||
try:
|
|
||||||
response = self.session.get(ESPN_NCAAMH_SCOREBOARD_URL, params={"dates": current_year, "limit":1000}, headers=self.headers, timeout=15)
|
|
||||||
response.raise_for_status()
|
|
||||||
data = response.json()
|
|
||||||
events = data.get('events', [])
|
|
||||||
|
|
||||||
if use_cache:
|
|
||||||
self.cache_manager.set(cache_key, events)
|
|
||||||
|
|
||||||
self.logger.info(f"Successfully fetched {len(events)} events for the {current_year} season.")
|
|
||||||
return {'events': events}
|
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
self.logger.error(f"[API error fetching full schedule: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _fetch_data(self) -> Optional[Dict]:
|
def _fetch_data(self) -> Optional[Dict]:
|
||||||
"""Fetch data using shared data mechanism or direct fetch for live."""
|
"""Fetch data using shared data mechanism or direct fetch for live."""
|
||||||
if isinstance(self, NCAAMHockeyLiveManager):
|
if isinstance(self, NCAAMHockeyLiveManager):
|
||||||
|
|||||||
@@ -97,10 +97,6 @@ class BaseNCAAWBasketballManager(Basketball):
|
|||||||
# Clear invalid cache
|
# Clear invalid cache
|
||||||
self.cache_manager.clear_cache(cache_key)
|
self.cache_manager.clear_cache(cache_key)
|
||||||
|
|
||||||
# If background service is disabled, fall back to synchronous fetch
|
|
||||||
if not self.background_enabled or not self.background_service:
|
|
||||||
return self._fetch_ncaaw_basketball_api_data_sync(use_cache)
|
|
||||||
|
|
||||||
# Start background fetch
|
# Start background fetch
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"Starting background fetch for {season_year} season schedule..."
|
f"Starting background fetch for {season_year} season schedule..."
|
||||||
@@ -151,41 +147,6 @@ class BaseNCAAWBasketballManager(Basketball):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _fetch_ncaaw_basketball_api_data_sync(
|
|
||||||
self, use_cache: bool = True
|
|
||||||
) -> Optional[Dict]:
|
|
||||||
"""
|
|
||||||
Synchronous fallback for fetching NFL data when background service is disabled.
|
|
||||||
"""
|
|
||||||
now = datetime.now(pytz.utc)
|
|
||||||
current_year = now.year
|
|
||||||
cache_key = f"ncaa_womens_basketball_schedule_{current_year}"
|
|
||||||
|
|
||||||
self.logger.info(
|
|
||||||
f"Fetching full {current_year} season schedule from ESPN API (sync mode)..."
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
response = self.session.get(
|
|
||||||
ESPN_NCAAWB_SCOREBOARD_URL,
|
|
||||||
params={"dates": current_year, "limit": 1000},
|
|
||||||
headers=self.headers,
|
|
||||||
timeout=15,
|
|
||||||
)
|
|
||||||
response.raise_for_status()
|
|
||||||
data = response.json()
|
|
||||||
events = data.get("events", [])
|
|
||||||
|
|
||||||
if use_cache:
|
|
||||||
self.cache_manager.set(cache_key, events)
|
|
||||||
|
|
||||||
self.logger.info(
|
|
||||||
f"Successfully fetched {len(events)} events for the {current_year} season."
|
|
||||||
)
|
|
||||||
return {"events": events}
|
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
self.logger.error(f"API error fetching full schedule: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _fetch_data(self) -> Optional[Dict]:
|
def _fetch_data(self) -> Optional[Dict]:
|
||||||
"""Fetch data using shared data mechanism or direct fetch for live."""
|
"""Fetch data using shared data mechanism or direct fetch for live."""
|
||||||
if isinstance(self, NCAAWBasketballLiveManager):
|
if isinstance(self, NCAAWBasketballLiveManager):
|
||||||
|
|||||||
@@ -92,10 +92,6 @@ class BaseNCAAWHockeyManager(Hockey): # Renamed class
|
|||||||
# Clear invalid cache
|
# Clear invalid cache
|
||||||
self.cache_manager.clear_cache(cache_key)
|
self.cache_manager.clear_cache(cache_key)
|
||||||
|
|
||||||
# If background service is disabled, fall back to synchronous fetch
|
|
||||||
if not self.background_enabled or not self.background_service:
|
|
||||||
return self._fetch_ncaa_api_data_sync(use_cache)
|
|
||||||
|
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"Fetching full {season_year} season schedule from ESPN API..."
|
f"Fetching full {season_year} season schedule from ESPN API..."
|
||||||
)
|
)
|
||||||
@@ -149,39 +145,6 @@ class BaseNCAAWHockeyManager(Hockey): # Renamed class
|
|||||||
return partial_data
|
return partial_data
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _fetch_ncaa_api_data_sync(self, use_cache: bool = True) -> Optional[Dict]:
|
|
||||||
"""
|
|
||||||
Synchronous fallback for fetching NCAA Womens Hockey data when background service is disabled.
|
|
||||||
"""
|
|
||||||
now = datetime.now(pytz.utc)
|
|
||||||
current_year = now.year
|
|
||||||
cache_key = f"ncaa_womens_hockey_schedule_{current_year}"
|
|
||||||
|
|
||||||
self.logger.info(
|
|
||||||
f"Fetching full {current_year} season schedule from ESPN API (sync mode)..."
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
response = self.session.get(
|
|
||||||
ESPN_NCAAWH_SCOREBOARD_URL,
|
|
||||||
params={"dates": current_year, "limit": 1000},
|
|
||||||
headers=self.headers,
|
|
||||||
timeout=15,
|
|
||||||
)
|
|
||||||
response.raise_for_status()
|
|
||||||
data = response.json()
|
|
||||||
events = data.get("events", [])
|
|
||||||
|
|
||||||
if use_cache:
|
|
||||||
self.cache_manager.set(cache_key, events)
|
|
||||||
|
|
||||||
self.logger.info(
|
|
||||||
f"Successfully fetched {len(events)} events for the {current_year} season."
|
|
||||||
)
|
|
||||||
return {"events": events}
|
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
self.logger.error(f"[API error fetching full schedule: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _fetch_data(self) -> Optional[Dict]:
|
def _fetch_data(self) -> Optional[Dict]:
|
||||||
"""Fetch data using shared data mechanism or direct fetch for live."""
|
"""Fetch data using shared data mechanism or direct fetch for live."""
|
||||||
if isinstance(self, NCAAWHockeyLiveManager):
|
if isinstance(self, NCAAWHockeyLiveManager):
|
||||||
|
|||||||
@@ -68,10 +68,6 @@ class BaseNFLManager(Football): # Renamed class
|
|||||||
# Clear invalid cache
|
# Clear invalid cache
|
||||||
self.cache_manager.clear_cache(cache_key)
|
self.cache_manager.clear_cache(cache_key)
|
||||||
|
|
||||||
# If background service is disabled, fall back to synchronous fetch
|
|
||||||
if not self.background_enabled or not self.background_service:
|
|
||||||
return self._fetch_nfl_api_data_sync(use_cache)
|
|
||||||
|
|
||||||
# Start background fetch
|
# Start background fetch
|
||||||
self.logger.info(f"Starting background fetch for {season_year} season schedule...")
|
self.logger.info(f"Starting background fetch for {season_year} season schedule...")
|
||||||
|
|
||||||
@@ -116,30 +112,6 @@ class BaseNFLManager(Football): # Renamed class
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _fetch_nfl_api_data_sync(self, use_cache: bool = True) -> Optional[Dict]:
|
|
||||||
"""
|
|
||||||
Synchronous fallback for fetching NFL data when background service is disabled.
|
|
||||||
"""
|
|
||||||
now = datetime.now(pytz.utc)
|
|
||||||
current_year = now.year
|
|
||||||
cache_key = f"nfl_schedule_{current_year}"
|
|
||||||
|
|
||||||
self.logger.info(f"Fetching full {current_year} season schedule from ESPN API (sync mode)...")
|
|
||||||
try:
|
|
||||||
response = self.session.get(ESPN_NFL_SCOREBOARD_URL, params={"dates": current_year, "limit":1000}, headers=self.headers, timeout=15)
|
|
||||||
response.raise_for_status()
|
|
||||||
data = response.json()
|
|
||||||
events = data.get('events', [])
|
|
||||||
|
|
||||||
if use_cache:
|
|
||||||
self.cache_manager.set(cache_key, events)
|
|
||||||
|
|
||||||
self.logger.info(f"Successfully fetched {len(events)} events for the {current_year} season.")
|
|
||||||
return {'events': events}
|
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
self.logger.error(f"API error fetching full schedule: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _fetch_data(self) -> Optional[Dict]:
|
def _fetch_data(self) -> Optional[Dict]:
|
||||||
"""Fetch data using shared data mechanism or direct fetch for live."""
|
"""Fetch data using shared data mechanism or direct fetch for live."""
|
||||||
if isinstance(self, NFLLiveManager):
|
if isinstance(self, NFLLiveManager):
|
||||||
|
|||||||
@@ -77,10 +77,6 @@ class BaseNHLManager(Hockey):
|
|||||||
# Clear invalid cache
|
# Clear invalid cache
|
||||||
self.cache_manager.clear_cache(cache_key)
|
self.cache_manager.clear_cache(cache_key)
|
||||||
|
|
||||||
# If background service is disabled, fall back to synchronous fetch
|
|
||||||
if not self.background_enabled or not self.background_service:
|
|
||||||
return self._fetch_nhl_api_data_sync(use_cache)
|
|
||||||
|
|
||||||
# Start background fetch
|
# Start background fetch
|
||||||
self.logger.info(f"Starting background fetch for {season_year} season schedule...")
|
self.logger.info(f"Starting background fetch for {season_year} season schedule...")
|
||||||
|
|
||||||
@@ -125,30 +121,6 @@ class BaseNHLManager(Hockey):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _fetch_nhl_api_data_sync(self, use_cache: bool = True) -> Optional[Dict]:
|
|
||||||
"""
|
|
||||||
Synchronous fallback for fetching NFL data when background service is disabled.
|
|
||||||
"""
|
|
||||||
now = datetime.now(pytz.utc)
|
|
||||||
current_year = now.year
|
|
||||||
cache_key = f"nhl_schedule_{current_year}"
|
|
||||||
|
|
||||||
self.logger.info(f"Fetching full {current_year} season schedule from ESPN API (sync mode)...")
|
|
||||||
try:
|
|
||||||
response = self.session.get(ESPN_NHL_SCOREBOARD_URL, params={"dates": current_year, "limit":1000}, headers=self.headers, timeout=15)
|
|
||||||
response.raise_for_status()
|
|
||||||
data = response.json()
|
|
||||||
events = data.get('events', [])
|
|
||||||
|
|
||||||
if use_cache:
|
|
||||||
self.cache_manager.set(cache_key, events)
|
|
||||||
|
|
||||||
self.logger.info(f"Successfully fetched {len(events)} events for the {current_year} season.")
|
|
||||||
return {'events': events}
|
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
self.logger.error(f"API error fetching full schedule: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _fetch_data(self, date_str: str = None) -> Optional[Dict]:
|
def _fetch_data(self, date_str: str = None) -> Optional[Dict]:
|
||||||
"""Fetch data using shared data mechanism or direct fetch for live."""
|
"""Fetch data using shared data mechanism or direct fetch for live."""
|
||||||
if isinstance(self, NHLLiveManager):
|
if isinstance(self, NHLLiveManager):
|
||||||
|
|||||||
@@ -113,19 +113,12 @@ class OddsTickerManager:
|
|||||||
# OddsManager doesn't actually use the config_manager parameter, so pass None
|
# OddsManager doesn't actually use the config_manager parameter, so pass None
|
||||||
self.odds_manager = OddsManager(self.cache_manager, None)
|
self.odds_manager = OddsManager(self.cache_manager, None)
|
||||||
|
|
||||||
# Initialize background data service
|
# Initialize background data service with optimized settings
|
||||||
background_config = self.odds_ticker_config.get("background_service", {})
|
# Hardcoded for memory optimization: 1 worker, 30s timeout, 3 retries
|
||||||
if background_config.get("enabled", True): # Default to enabled
|
self.background_service = get_background_service(self.cache_manager, max_workers=1)
|
||||||
max_workers = background_config.get("max_workers", 3)
|
|
||||||
self.background_service = get_background_service(self.cache_manager, max_workers)
|
|
||||||
self.background_fetch_requests = {} # Track background fetch requests
|
self.background_fetch_requests = {} # Track background fetch requests
|
||||||
self.background_enabled = True
|
self.background_enabled = True
|
||||||
logger.info(f"[Odds Ticker] Background service enabled with {max_workers} workers")
|
logger.info("[Odds Ticker] Background service enabled with 1 worker (memory optimized)")
|
||||||
else:
|
|
||||||
self.background_service = None
|
|
||||||
self.background_fetch_requests = {}
|
|
||||||
self.background_enabled = False
|
|
||||||
logger.info("[Odds Ticker] Background service disabled")
|
|
||||||
|
|
||||||
# State variables
|
# State variables
|
||||||
self.last_update = 0
|
self.last_update = 0
|
||||||
|
|||||||
@@ -93,19 +93,12 @@ class BaseSoccerManager:
|
|||||||
|
|
||||||
self._logo_cache = {}
|
self._logo_cache = {}
|
||||||
|
|
||||||
# Initialize background data service
|
# Initialize background data service with optimized settings
|
||||||
background_config = self.soccer_config.get("background_service", {})
|
# Hardcoded for memory optimization: 1 worker, 30s timeout, 3 retries
|
||||||
if background_config.get("enabled", True): # Default to enabled
|
self.background_service = get_background_service(self.cache_manager, max_workers=1)
|
||||||
max_workers = background_config.get("max_workers", 3)
|
|
||||||
self.background_service = get_background_service(self.cache_manager, max_workers)
|
|
||||||
self.background_fetch_requests = {} # Track background fetch requests
|
self.background_fetch_requests = {} # Track background fetch requests
|
||||||
self.background_enabled = True
|
self.background_enabled = True
|
||||||
self.logger.info(f"[Soccer] Background service enabled with {max_workers} workers")
|
self.logger.info("[Soccer] Background service enabled with 1 worker (memory optimized)")
|
||||||
else:
|
|
||||||
self.background_service = None
|
|
||||||
self.background_fetch_requests = {}
|
|
||||||
self.background_enabled = False
|
|
||||||
self.logger.info("[Soccer] Background service disabled")
|
|
||||||
|
|
||||||
# Ensure data directory exists
|
# Ensure data directory exists
|
||||||
os.makedirs(os.path.dirname(self.team_map_file), exist_ok=True)
|
os.makedirs(os.path.dirname(self.team_map_file), exist_ok=True)
|
||||||
|
|||||||
@@ -101,10 +101,6 @@ class BaseWNBAManager(Basketball):
|
|||||||
# Clear invalid cache
|
# Clear invalid cache
|
||||||
self.cache_manager.clear_cache(cache_key)
|
self.cache_manager.clear_cache(cache_key)
|
||||||
|
|
||||||
# If background service is disabled, fall back to synchronous fetch
|
|
||||||
if not self.background_enabled or not self.background_service:
|
|
||||||
return self._fetch_wnba_api_data_sync(use_cache)
|
|
||||||
|
|
||||||
# Start background fetch
|
# Start background fetch
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"Starting background fetch for {season_year} season schedule..."
|
f"Starting background fetch for {season_year} season schedule..."
|
||||||
@@ -155,39 +151,6 @@ class BaseWNBAManager(Basketball):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _fetch_wnba_api_data_sync(self, use_cache: bool = True) -> Optional[Dict]:
|
|
||||||
"""
|
|
||||||
Synchronous fallback for fetching WNBA data when background service is disabled.
|
|
||||||
"""
|
|
||||||
now = datetime.now(pytz.utc)
|
|
||||||
current_year = now.year
|
|
||||||
cache_key = f"nba_schedule_{current_year}"
|
|
||||||
|
|
||||||
self.logger.info(
|
|
||||||
f"Fetching full {current_year} season schedule from ESPN API (sync mode)..."
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
response = self.session.get(
|
|
||||||
ESPN_WNBA_SCOREBOARD_URL,
|
|
||||||
params={"dates": current_year, "limit": 1000},
|
|
||||||
headers=self.headers,
|
|
||||||
timeout=15,
|
|
||||||
)
|
|
||||||
response.raise_for_status()
|
|
||||||
data = response.json()
|
|
||||||
events = data.get("events", [])
|
|
||||||
|
|
||||||
if use_cache:
|
|
||||||
self.cache_manager.set(cache_key, events)
|
|
||||||
|
|
||||||
self.logger.info(
|
|
||||||
f"Successfully fetched {len(events)} events for the {current_year} season."
|
|
||||||
)
|
|
||||||
return {"events": events}
|
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
self.logger.error(f"API error fetching full schedule: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _fetch_data(self) -> Optional[Dict]:
|
def _fetch_data(self) -> Optional[Dict]:
|
||||||
"""Fetch data using shared data mechanism or direct fetch for live."""
|
"""Fetch data using shared data mechanism or direct fetch for live."""
|
||||||
if isinstance(self, WNBALiveManager):
|
if isinstance(self, WNBALiveManager):
|
||||||
|
|||||||
Reference in New Issue
Block a user