From 7a61ecff7b754cd5e4de6dffbc09d272be05ad02 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Wed, 8 Oct 2025 20:10:54 -0400 Subject: [PATCH] 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 --- LEDMatrix.wiki | 2 +- config/config.template.json | 73 +++----------------------------- src/base_classes/sports.py | 19 +++------ src/leaderboard_manager.py | 19 +++------ src/milb_manager.py | 19 +++------ src/mlb_manager.py | 5 --- src/nba_managers.py | 28 ------------ src/ncaa_baseball_managers.py | 5 --- src/ncaa_fb_managers.py | 28 ------------ src/ncaam_basketball_managers.py | 39 ----------------- src/ncaam_hockey_managers.py | 29 ------------- src/ncaaw_basketball_managers.py | 39 ----------------- src/ncaaw_hockey_managers.py | 37 ---------------- src/nfl_managers.py | 28 ------------ src/nhl_managers.py | 28 ------------ src/odds_ticker_manager.py | 19 +++------ src/soccer_managers.py | 19 +++------ src/wnba_managers.py | 37 ---------------- 18 files changed, 36 insertions(+), 437 deletions(-) diff --git a/LEDMatrix.wiki b/LEDMatrix.wiki index a01c72e1..fbd8d89a 160000 --- a/LEDMatrix.wiki +++ b/LEDMatrix.wiki @@ -1 +1 @@ -Subproject commit a01c72e156b46c08a5ef1c67db79acd73300a6f7 +Subproject commit fbd8d89a186e5757d1785737b0ee4c03ad442dbf diff --git a/config/config.template.json b/config/config.template.json index abc5d749..bf95b980 100644 --- a/config/config.template.json +++ b/config/config.template.json @@ -17,8 +17,8 @@ "cols": 64, "chain_length": 2, "parallel": 1, - "brightness": 95, - "hardware_mapping": "adafruit-hat-pwm", + "brightness": 90, + "hardware_mapping": "adafruit-hat", "scan_mode": 0, "pwm_bits": 9, "pwm_dither_bits": 1, @@ -26,7 +26,7 @@ "disable_hardware_pulsing": false, "inverse_colors": false, "show_refresh_rate": false, - "limit_refresh_rate_hz": 120 + "limit_refresh_rate_hz": 100 }, "runtime": { "gpio_slowdown": 3 @@ -152,14 +152,7 @@ "dynamic_duration": true, "min_duration": 30, "max_duration": 300, - "duration_buffer": 0.1, - "background_service": { - "enabled": true, - "max_workers": 3, - "request_timeout": 30, - "max_retries": 3, - "priority": 2 - } + "duration_buffer": 0.1 }, "leaderboard": { "enabled": false, @@ -202,14 +195,7 @@ "request_timeout": 30, "dynamic_duration": true, "min_duration": 30, - "max_display_time": 600, - "background_service": { - "enabled": true, - "max_workers": 3, - "request_timeout": 30, - "max_retries": 3, - "priority": 2 - } + "max_display_time": 600 }, "calendar": { "enabled": false, @@ -241,13 +227,6 @@ ], "logo_dir": "assets/sports/nhl_logos", "show_records": true, - "background_service": { - "enabled": true, - "max_workers": 3, - "request_timeout": 30, - "max_retries": 3, - "priority": 2 - }, "display_modes": { "nhl_live": true, "nhl_recent": true, @@ -275,13 +254,6 @@ ], "logo_dir": "assets/sports/nba_logos", "show_records": true, - "background_service": { - "enabled": true, - "max_workers": 3, - "request_timeout": 30, - "max_retries": 3, - "priority": 2 - }, "display_modes": { "nba_live": true, "nba_recent": true, @@ -309,13 +281,6 @@ ], "logo_dir": "assets/sports/wnba_logos", "show_records": true, - "background_service": { - "enabled": true, - "max_workers": 3, - "request_timeout": 30, - "max_retries": 3, - "priority": 2 - }, "display_modes": { "wnba_live": true, "wnba_recent": true, @@ -344,13 +309,6 @@ ], "logo_dir": "assets/sports/nfl_logos", "show_records": true, - "background_service": { - "enabled": true, - "max_workers": 3, - "request_timeout": 30, - "max_retries": 3, - "priority": 2 - }, "display_modes": { "nfl_live": true, "nfl_recent": true, @@ -378,13 +336,6 @@ "logo_dir": "assets/sports/ncaa_logos", "show_records": true, "show_ranking": true, - "background_service": { - "enabled": true, - "max_workers": 3, - "request_timeout": 30, - "max_retries": 3, - "priority": 2 - }, "display_modes": { "ncaa_fb_live": true, "ncaa_fb_recent": true, @@ -570,13 +521,6 @@ "logo_dir": "assets/sports/milb_logos", "show_records": true, "upcoming_fetch_days": 7, - "background_service": { - "enabled": true, - "max_workers": 3, - "request_timeout": 30, - "max_retries": 3, - "priority": 2 - }, "display_modes": { "milb_live": true, "milb_recent": true, @@ -623,13 +567,6 @@ ], "logo_dir": "assets/sports/soccer_logos", "show_records": true, - "background_service": { - "enabled": true, - "max_workers": 3, - "request_timeout": 30, - "max_retries": 3, - "priority": 2 - }, "display_modes": { "soccer_live": true, "soccer_recent": true, diff --git a/src/base_classes/sports.py b/src/base_classes/sports.py index 24e502e8..3c1bade9 100644 --- a/src/base_classes/sports.py +++ b/src/base_classes/sports.py @@ -105,19 +105,12 @@ class SportsCore(ABC): self._rankings_cache_timestamp = 0 self._rankings_cache_duration = 3600 # Cache rankings for 1 hour - # Initialize background data service - background_config = self.mode_config.get("background_service", {}) - if background_config.get("enabled", True): # Default to enabled - 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_enabled = True - self.logger.info(f"Background service enabled with {max_workers} workers") - else: - self.background_service = None - self.background_fetch_requests = {} - self.background_enabled = False - self.logger.info("Background service disabled") + # Initialize background data service with optimized settings + # Hardcoded for memory optimization: 1 worker, 30s timeout, 3 retries + self.background_service = get_background_service(self.cache_manager, max_workers=1) + self.background_fetch_requests = {} # Track background fetch requests + self.background_enabled = True + self.logger.info("Background service enabled with 1 worker (memory optimized)") def _get_season_schedule_dates(self) -> tuple[str, str]: return "", "" diff --git a/src/leaderboard_manager.py b/src/leaderboard_manager.py index 975c0c05..11c86940 100644 --- a/src/leaderboard_manager.py +++ b/src/leaderboard_manager.py @@ -71,19 +71,12 @@ class LeaderboardManager: # Store reference to config instead of creating new ConfigManager self.config = config - # Initialize background data service - background_config = self.leaderboard_config.get("background_service", {}) - if background_config.get("enabled", True): # Default to enabled - 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_enabled = True - logger.info(f"[Leaderboard] Background service enabled with {max_workers} workers") - else: - self.background_service = None - self.background_fetch_requests = {} - self.background_enabled = False - logger.info("[Leaderboard] Background service disabled") + # Initialize background data service with optimized settings + # Hardcoded for memory optimization: 1 worker, 30s timeout, 3 retries + self.background_service = get_background_service(self.cache_manager, max_workers=1) + self.background_fetch_requests = {} # Track background fetch requests + self.background_enabled = True + logger.info("[Leaderboard] Background service enabled with 1 worker (memory optimized)") # State variables self.last_update = 0 diff --git a/src/milb_manager.py b/src/milb_manager.py index 1b692df1..9a2fedc2 100644 --- a/src/milb_manager.py +++ b/src/milb_manager.py @@ -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' } - # Initialize background data service - background_config = self.milb_config.get("background_service", {}) - if background_config.get("enabled", True): # Default to enabled - 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_enabled = True - self.logger.info(f"[MiLB] Background service enabled with {max_workers} workers") - else: - self.background_service = None - self.background_fetch_requests = {} - self.background_enabled = False - self.logger.info("[MiLB] Background service disabled") + # Initialize background data service with optimized settings + # Hardcoded for memory optimization: 1 worker, 30s timeout, 3 retries + self.background_service = get_background_service(self.cache_manager, max_workers=1) + self.background_fetch_requests = {} # Track background fetch requests + self.background_enabled = True + self.logger.info("[MiLB] Background service enabled with 1 worker (memory optimized)") 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. diff --git a/src/mlb_manager.py b/src/mlb_manager.py index 25a79628..757a1dfc 100644 --- a/src/mlb_manager.py +++ b/src/mlb_manager.py @@ -82,11 +82,6 @@ class BaseMLBManager(Baseball): # Clear invalid cache 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...") # Start background fetch diff --git a/src/nba_managers.py b/src/nba_managers.py index d89197b7..7cfba9c2 100644 --- a/src/nba_managers.py +++ b/src/nba_managers.py @@ -76,10 +76,6 @@ class BaseNBAManager(Basketball): # Clear invalid cache 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 self.logger.info(f"Starting background fetch for {season_year} season schedule...") @@ -123,30 +119,6 @@ class BaseNBAManager(Basketball): return partial_data 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]: """Fetch data using shared data mechanism or direct fetch for live.""" diff --git a/src/ncaa_baseball_managers.py b/src/ncaa_baseball_managers.py index 0556f1e8..a7954f96 100644 --- a/src/ncaa_baseball_managers.py +++ b/src/ncaa_baseball_managers.py @@ -60,11 +60,6 @@ class BaseNCAABaseballManager(Baseball): # Clear invalid cache 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...") # Start background fetch diff --git a/src/ncaa_fb_managers.py b/src/ncaa_fb_managers.py index 8054fdd0..00d21b5f 100644 --- a/src/ncaa_fb_managers.py +++ b/src/ncaa_fb_managers.py @@ -73,10 +73,6 @@ class BaseNCAAFBManager(Football): # Renamed class # Clear invalid cache 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...") # Start background fetch @@ -121,30 +117,6 @@ class BaseNCAAFBManager(Football): # Renamed class if partial_data: return partial_data 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]: """Fetch data using shared data mechanism or direct fetch for live.""" diff --git a/src/ncaam_basketball_managers.py b/src/ncaam_basketball_managers.py index fad9f52d..45d4a953 100644 --- a/src/ncaam_basketball_managers.py +++ b/src/ncaam_basketball_managers.py @@ -97,10 +97,6 @@ class BaseNCAAMBasketballManager(Basketball): # Clear invalid cache 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 self.logger.info( f"Starting background fetch for {season_year} season schedule..." @@ -151,41 +147,6 @@ class BaseNCAAMBasketballManager(Basketball): 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]: """Fetch data using shared data mechanism or direct fetch for live.""" if isinstance(self, NCAAMBasketballLiveManager): diff --git a/src/ncaam_hockey_managers.py b/src/ncaam_hockey_managers.py index 0c240155..a208b916 100644 --- a/src/ncaam_hockey_managers.py +++ b/src/ncaam_hockey_managers.py @@ -77,10 +77,6 @@ class BaseNCAAMHockeyManager(Hockey): # Renamed class # Clear invalid cache 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...") # Start background fetch @@ -126,31 +122,6 @@ class BaseNCAAMHockeyManager(Hockey): # Renamed class return partial_data 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]: """Fetch data using shared data mechanism or direct fetch for live.""" if isinstance(self, NCAAMHockeyLiveManager): diff --git a/src/ncaaw_basketball_managers.py b/src/ncaaw_basketball_managers.py index 2b865e0e..2d402109 100644 --- a/src/ncaaw_basketball_managers.py +++ b/src/ncaaw_basketball_managers.py @@ -97,10 +97,6 @@ class BaseNCAAWBasketballManager(Basketball): # Clear invalid cache 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 self.logger.info( f"Starting background fetch for {season_year} season schedule..." @@ -151,41 +147,6 @@ class BaseNCAAWBasketballManager(Basketball): 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]: """Fetch data using shared data mechanism or direct fetch for live.""" if isinstance(self, NCAAWBasketballLiveManager): diff --git a/src/ncaaw_hockey_managers.py b/src/ncaaw_hockey_managers.py index 9ff82c09..f6dd54b4 100644 --- a/src/ncaaw_hockey_managers.py +++ b/src/ncaaw_hockey_managers.py @@ -92,10 +92,6 @@ class BaseNCAAWHockeyManager(Hockey): # Renamed class # Clear invalid cache 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..." ) @@ -149,39 +145,6 @@ class BaseNCAAWHockeyManager(Hockey): # Renamed class return partial_data 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]: """Fetch data using shared data mechanism or direct fetch for live.""" if isinstance(self, NCAAWHockeyLiveManager): diff --git a/src/nfl_managers.py b/src/nfl_managers.py index 49e7ff2b..24c9f944 100644 --- a/src/nfl_managers.py +++ b/src/nfl_managers.py @@ -68,10 +68,6 @@ class BaseNFLManager(Football): # Renamed class # Clear invalid cache 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 self.logger.info(f"Starting background fetch for {season_year} season schedule...") @@ -115,30 +111,6 @@ class BaseNFLManager(Football): # Renamed class return partial_data 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]: """Fetch data using shared data mechanism or direct fetch for live.""" diff --git a/src/nhl_managers.py b/src/nhl_managers.py index 887c0e9f..5b529657 100644 --- a/src/nhl_managers.py +++ b/src/nhl_managers.py @@ -77,10 +77,6 @@ class BaseNHLManager(Hockey): # Clear invalid cache 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 self.logger.info(f"Starting background fetch for {season_year} season schedule...") @@ -124,30 +120,6 @@ class BaseNHLManager(Hockey): return partial_data 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]: """Fetch data using shared data mechanism or direct fetch for live.""" diff --git a/src/odds_ticker_manager.py b/src/odds_ticker_manager.py index 0de9ac5a..ff259b9b 100644 --- a/src/odds_ticker_manager.py +++ b/src/odds_ticker_manager.py @@ -113,19 +113,12 @@ class OddsTickerManager: # OddsManager doesn't actually use the config_manager parameter, so pass None self.odds_manager = OddsManager(self.cache_manager, None) - # Initialize background data service - background_config = self.odds_ticker_config.get("background_service", {}) - if background_config.get("enabled", True): # Default to enabled - 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_enabled = True - logger.info(f"[Odds Ticker] Background service enabled with {max_workers} workers") - else: - self.background_service = None - self.background_fetch_requests = {} - self.background_enabled = False - logger.info("[Odds Ticker] Background service disabled") + # Initialize background data service with optimized settings + # Hardcoded for memory optimization: 1 worker, 30s timeout, 3 retries + self.background_service = get_background_service(self.cache_manager, max_workers=1) + self.background_fetch_requests = {} # Track background fetch requests + self.background_enabled = True + logger.info("[Odds Ticker] Background service enabled with 1 worker (memory optimized)") # State variables self.last_update = 0 diff --git a/src/soccer_managers.py b/src/soccer_managers.py index cd0757af..590f827c 100644 --- a/src/soccer_managers.py +++ b/src/soccer_managers.py @@ -93,19 +93,12 @@ class BaseSoccerManager: self._logo_cache = {} - # Initialize background data service - background_config = self.soccer_config.get("background_service", {}) - if background_config.get("enabled", True): # Default to enabled - 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_enabled = True - self.logger.info(f"[Soccer] Background service enabled with {max_workers} workers") - else: - self.background_service = None - self.background_fetch_requests = {} - self.background_enabled = False - self.logger.info("[Soccer] Background service disabled") + # Initialize background data service with optimized settings + # Hardcoded for memory optimization: 1 worker, 30s timeout, 3 retries + self.background_service = get_background_service(self.cache_manager, max_workers=1) + self.background_fetch_requests = {} # Track background fetch requests + self.background_enabled = True + self.logger.info("[Soccer] Background service enabled with 1 worker (memory optimized)") # Ensure data directory exists os.makedirs(os.path.dirname(self.team_map_file), exist_ok=True) diff --git a/src/wnba_managers.py b/src/wnba_managers.py index 88fc918f..01c522fa 100644 --- a/src/wnba_managers.py +++ b/src/wnba_managers.py @@ -101,10 +101,6 @@ class BaseWNBAManager(Basketball): # Clear invalid cache 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 self.logger.info( f"Starting background fetch for {season_year} season schedule..." @@ -155,39 +151,6 @@ class BaseWNBAManager(Basketball): 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]: """Fetch data using shared data mechanism or direct fetch for live.""" if isinstance(self, WNBALiveManager):