class IgdbSyncJob < ApplicationJob queue_as :default # Ensure only one instance runs at a time def self.running? Rails.cache.exist?("igdb_sync_job:running") end def self.mark_running! Rails.cache.write("igdb_sync_job:running", true, expires_in: 2.hours) end def self.mark_finished! Rails.cache.delete("igdb_sync_job:running") end def perform # Prevent multiple instances if self.class.running? Rails.logger.info("IgdbSyncJob already running, skipping...") return end self.class.mark_running! begin sync_users_with_igdb ensure self.class.mark_finished! end end private def sync_users_with_igdb users = User.where(igdb_sync_enabled: true) Rails.logger.info("Starting IGDB sync for #{users.count} users") users.find_each do |user| sync_user_games(user) rescue => e Rails.logger.error("Error syncing user #{user.id}: #{e.message}") next end Rails.logger.info("IGDB sync completed") end def sync_user_games(user) # Get games that need IGDB matching games = user.games .igdb_unmatched .where(igdb_match_status: [ nil, "failed" ]) .includes(:platform) return if games.empty? Rails.logger.info("Syncing #{games.count} games for user #{user.id}") igdb_service = IgdbService.new games_synced = 0 games.find_each do |game| process_game_matching(game, igdb_service) games_synced += 1 # Rate limiting: Additional sleep every 10 games sleep(1) if games_synced % 10 == 0 rescue => e Rails.logger.error("Error processing game #{game.id}: #{e.message}") game.update(igdb_match_status: "failed") next end user.update(igdb_last_synced_at: Time.current) end def process_game_matching(game, igdb_service) Rails.logger.info("Searching IGDB for: #{game.title} (#{game.platform.name})") # Try searching WITH platform first results = igdb_service.search_game(game.title, game.platform, 3) # If no results, try WITHOUT platform (broader search) if results.empty? Rails.logger.info("No results with platform, trying without platform filter...") results = igdb_service.search_game(game.title, nil, 3) end if results.empty? Rails.logger.info("No IGDB matches found for game #{game.id}") game.update(igdb_match_status: "no_results") return end Rails.logger.info("Found #{results.count} potential matches for game #{game.id}") # Create match suggestions for user review results.each do |result| create_match_suggestion(game, result) end # Update game status if results.first[:confidence_score] >= 95.0 # Very high confidence - could auto-approve, but we'll let user review game.update(igdb_match_status: "high_confidence") elsif results.first[:confidence_score] >= 70.0 game.update(igdb_match_status: "medium_confidence") else game.update(igdb_match_status: "low_confidence") end end def create_match_suggestion(game, result) # Skip if suggestion already exists existing = IgdbMatchSuggestion.find_by(game: game, igdb_id: result[:igdb_id]) return if existing IgdbMatchSuggestion.create!( game: game, igdb_id: result[:igdb_id], igdb_name: result[:name], igdb_slug: result[:slug], igdb_cover_url: result[:cover_url], igdb_summary: result[:summary], igdb_release_date: result[:release_date], igdb_platform_name: result[:platform_name], igdb_genres: result[:genres] || [], confidence_score: result[:confidence_score], status: "pending" ) Rails.logger.info("Created match suggestion: #{result[:name]} (confidence: #{result[:confidence_score]}%)") rescue ActiveRecord::RecordInvalid => e Rails.logger.warn("Failed to create match suggestion: #{e.message}") end end