Skip to content

Commit a2c5dcb

Browse files
committed
fix: solve single-query no vector builder.
1 parent 1e59933 commit a2c5dcb

3 files changed

Lines changed: 60 additions & 21 deletions

File tree

app/modules/ai_intelligence/jobs/rebuild_champion_matrix_job.rb

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,25 @@ class RebuildChampionMatrixJob < ApplicationJob
88
queue_as :low_priority
99

1010
def perform(scope: :all, league: nil)
11-
Rails.logger.info("[AI] Starting champion matrix rebuild scope=#{scope} league=#{league}")
11+
lock_key = "sidekiq:rebuild_champion_matrix:lock"
12+
acquired = Sidekiq.redis { |r| r.call("SET", lock_key, "1", "NX", "EX", 3600) }
13+
14+
unless acquired
15+
Rails.logger.info("[AI] RebuildChampionMatrixJob skipped — already running")
16+
return
17+
end
18+
19+
rebuild_matrices(scope:, league:)
20+
ensure
21+
Sidekiq.redis { |r| r.call("DEL", lock_key) } if acquired
22+
end
1223

24+
private
25+
26+
def rebuild_matrices(scope:, league:)
27+
Rails.logger.info("[AI] Starting champion matrix rebuild scope=#{scope} league=#{league}")
1328
ChampionMatrixBuilder.call(scope: scope.to_sym, league:)
1429
ChampionVectorBuilder.rebuild_all!
15-
1630
Rails.logger.info("[AI] Champion matrices rebuilt at #{Time.current}")
1731
end
1832
end

app/modules/ai_intelligence/services/champion_vector_builder.rb

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,8 @@ def self.call(champion_name:, league: nil)
1717
end
1818

1919
def self.rebuild_all!
20-
champion_names = extract_all_champion_names
21-
champion_names.each do |name|
22-
vector = call(champion_name: name)
23-
next if vector.nil?
24-
25-
appearances_count = new(champion_name: name).send(:all_appearances).size
26-
27-
AiChampionVector.find_or_initialize_by(champion_name: name).tap do |v|
28-
v.vector_data = vector.to_a
29-
v.games_count = appearances_count
30-
v.updated_at = Time.current
31-
v.save!
32-
end
33-
end
20+
all_matches = CompetitiveMatch.unscoped.to_a
21+
collect_champion_names(all_matches).each { |name| persist_vector(name, all_matches) }
3422
end
3523

3624
def build
@@ -47,15 +35,46 @@ def build
4735
normalize_vector(vector)
4836
end
4937

50-
def self.extract_all_champion_names
51-
CompetitiveMatch.unscoped.flat_map do |m|
52-
picks = (m.our_picks || []) + (m.opponent_picks || [])
53-
picks.map { |p| p['champion'] }
54-
end.compact.uniq
38+
def appearances_from_preloaded(matches)
39+
filtered = @league ? matches.select { |m| m.tournament_name == @league } : matches
40+
filtered.flat_map { |match| extract_from_match(match) }
41+
end
42+
43+
def build_from_appearances(appearances)
44+
arrays = extract_stat_arrays(appearances)
45+
stats = build_stat_hash(appearances.size, arrays)
46+
vector = Numo::DFloat[
47+
stats[:win_rate], stats[:avg_kda], stats[:avg_damage_share],
48+
stats[:avg_gold_share], normalize(stats[:avg_cs], 0, 400)
49+
]
50+
normalize_vector(vector)
5551
end
5652

5753
private
5854

55+
def self.collect_champion_names(matches)
56+
matches.flat_map { |m|
57+
((m.our_picks || []) + (m.opponent_picks || [])).map { |p| p['champion'] }
58+
}.compact.uniq
59+
end
60+
private_class_method :collect_champion_names
61+
62+
def self.persist_vector(champion_name, all_matches)
63+
builder = new(champion_name: champion_name)
64+
appearances = builder.appearances_from_preloaded(all_matches)
65+
return if appearances.empty?
66+
67+
vector = builder.build_from_appearances(appearances)
68+
69+
AiChampionVector.find_or_initialize_by(champion_name: champion_name).tap do |v|
70+
v.vector_data = vector.to_a
71+
v.games_count = appearances.size
72+
v.updated_at = Time.current
73+
v.save!
74+
end
75+
end
76+
private_class_method :persist_vector
77+
5978
def aggregate_stats
6079
appearances = all_appearances
6180
return { games: 0 } if appearances.empty?

config/initializers/sidekiq.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,13 @@ def configure_sidekiq_with_retry # rubocop:disable Metrics/AbcSize
3636
pool_timeout: 5
3737
}
3838

39+
config.logger = Sidekiq::Logger.new($stdout, level: :info)
40+
config.logger.formatter = Sidekiq::Logger::Formatters::JSON.new
41+
3942
config.on(:startup) do
43+
Rails.logger = Sidekiq.logger
44+
ActiveRecord::Base.logger = nil
45+
4046
schedule_file = Rails.root.join('config', 'sidekiq.yml')
4147
if File.exist?(schedule_file)
4248
schedule = YAML.load_file(schedule_file)

0 commit comments

Comments
 (0)