Reputation: 25542
Here is my Sidekiq
setup:
Controller that calls my background process
BackgroundStatCalculator.perform_async @game.athlete.id, @game.id, current_athlete_sport.id
Sidekiq worker:
class BackgroundStatCalculator
include Sidekiq::Worker
def perform(user_id, game_id, sport_id)
sport = Sport.find sport_id
sport.stat_type_categories.each do |st_cat|
calc = "StatCalculators::#{st_cat.name}".constantize.new(user_id, game_id)
calc.run_calculations
end
end
end
Calculator:
module StatCalculators
class Passing
def initialize(user_id, game_id)
@user = User.find(user_id)
@game = Game.find(game_id)
end
def save_completion_percentage
completions = Stat.for_type("Completions").for_user(@user).sum(:float_value)
attempts = Stat.for_type("Pass Attempts").for_user(@user).sum(:float_value)
value = completions/attempts
stat = Stat.new(value: value, game_id: @game.id, athlete_id: @user.id, float_value: value)
stat.save(validate: false)
end
def run_calculations
klass = self.class
klass.instance_methods(false).each do |method|
klass.instance_method(method).bind(self).call
end
end
end
end
StackTrace:
2013-06-07T17:55:34Z 73625 TID-ov6v51sww BackgroundStatCalculator JID-5bab7cec30523a4b12dd6438 INFO: fail: 5.155 sec
2013-06-07T17:55:34Z 73625 TID-ov6v51sww WARN: {"retry"=>true, "queue"=>"default", "class"=>"BackgroundStatCalculator", "args"=>[58, 68, 6], "jid"=>"5bab7cec30523a4b12dd6438", "error_message"=>"stack level too deep", "error_class"=>"SystemStackError", "failed_at"=>"2013-06-07T17:42:01Z", "retry_count"=>5, "retried_at"=>2013-06-07 17:55:34 UTC}
2013-06-07T17:55:34Z 73625 TID-ov6v51sww WARN: stack level too deep
2013-06-07T17:55:34Z 73625 TID-ov6v51sww WARN: /Users/dennismonsewicz/.rvm/gems/ruby-1.9.3-p429/gems/sidekiq-2.7.4/lib/sidekiq/processor.rb:88
For some reason when .perform_async
is called, it doesn't just execute once and done... it dumps 100s of rows into my database.
Anyone run into this issue before? I am fairly new to working with Sidekiq, so my apologies for my ignorance here
Upvotes: 1
Views: 852
Reputation: 19145
You've created an infinite loop of calls which is causing the stack to overflow.
Your run_calculations
method calls every instance method on the object it's on, which includes run_calculations
. You need to filter out this method from the list or better yet, find a way to white-list only the calculation methods you want to call. Perhaps using a prefix like "calculate_".
module StatCalculators
class Passing
def initialize(user_id, game_id)
...
end
def calculate_completion_percentage
...
end
def run_calculations
klass = self.class
calculation_methods = klass.instance_methods(false).select do |method|
method.to_s.match /calculate_/
end
calculation_methods.each do |method|
klass.instance_method(method).bind(self).call
end
end
end
end
Upvotes: 2