Search code examples
ruby-on-railsredisactionmailersidekiqworker

sidekiq worker rails argument error for one of two workers


I have a rails app using sidekiq w/ redis for mailers. I have 2 workers, but for one of them I get wrong number of arguments error (ArgumentError: wrong number of arguments (5 for 2) ) and I don't know why since the other one, which is basically the same, works perfectly. Here is the code/explanation for both: Contactmailer w/ Postmanworker is the working one which sends out an email when sby submits a contact form; Taskmailer w/ Taskcreatorworker gives the error, which sends out an email when a task gets created. I've already tried with different number of arguments, as I see I have 6 args at the moment but still gives the 5 for 2 error => Prior to sidekiq, taskmailer worked as well. For first I tried to pass only 2 args @task.id and @current_user and I could call all the 6 variables that can be found in task_created.html.erb. Since then I've tried different kind and number of args. As you see below, at the moment I try to give all the 6 variables in controller and mailer as well exactly as they are called. I also show the user.rb since model is a bit tricky.

Since the contact mailer works perfectly both in dev/prod env, I likely made a mistake in the code below and it's not a server/installation problem.

tasks_controller.rb

def create
  @user = current_user
  @task = Task.new(task_params))
  if @task.save
    h = JSON.generate( {'task_assigner_first_name' => @current_user.profile.first_name,
                      'task_assigner_last_name' => @current_user.profile.last_name,
                      'task_executor_first_name' => @task.executor.profile.first_name,
                      'task_executor_email' => @task.executor.email,
                      'task_executor_id' => @task.executor_id,
                      'task_id' => @task.id
                       } )
    TaskcreatorWorker.perform_async(h, 5)
    #TaskMailer.task_created(current_user, @task).deliver_later
    flash[:success] = "Task saved!"
    redirect_to user_tasks_path(current_user)
  else
    render action: :new
  end
end

private
def task_params
  params.require(:task).permit(:executor_id, :name, :content, :deadline).merge(assigner_id: current_user.id)
end

contacts_controller.rb

def create
  @contact = Contact.new(contact_params)
  if @contact.save
    h = JSON.generate({ 'name' => params[:contact][:name],
                      'email' => params[:contact][:email],
                      'comment' => params[:contact][:comment] })

    PostmanWorker.perform_async(h, 5)
    #ContactMailer.contact_email(name, email, comment).deliver_later
    flash[:success] = "Message sent."
    redirect_to new_contact_path
  else
    flash[:danger] = "Error occured."
    render action: :new
  end
end

private
def contact_params
  params.require(:contact).permit(:name, :email, :comment)
end

taskcreator_worker.rb

class TaskcreatorWorker
  include Sidekiq::Worker

  def perform(h, count)
    h = JSON.load(h)
    TaskMailer.task_created(h['task_assigner_first_name'], h['task_assigner_last_name'], h['task_executor_first_name'], h['task_executor_email'], h['task_executor_id'], h['task_id']).deliver_later
  end
end

postman_worker.rb

class PostmanWorker
  include Sidekiq::Worker

  def perform(h, count)
    h = JSON.load(h)
    ContactMailer.contact_email(h['name'],h['email'],h['comment']).deliver_later
  end 
end

task_mailer.rb

class TaskMailer < ActionMailer::Base

  def task_created(task_assigner_first_name, task_assigner_last_name, task_executor_first_name, task_executor_email, task_executor_id, task_id) 
    @task.assigner.profile.first_name = task_assigner_first_name
    @task.assigner.profile.last_name = task_assigner_last_name
    @task.executor.profile.first_name = task_executor_first_name
    @task.executor.email = task_executor_email
    @task.executor_id = task_executor_id
    @task.id = task_id

    mail(from: '[email protected]',
         to: "#{task.executor.email}",
         subject: "[Faskyn] New task/favor from #{task.assigner.profile.first_name} #{task.assigner.profile.last_name}"
         )
  end
end

contact_mailer.rb

class ContactMailer < ActionMailer::Base
  default to: '[email protected]'

  def contact_email(name, email, content)
    @name = name
    @email = email
    @content = content

    mail(from: email, subject: 'Contact form message')
  end
end

task_created.html.erb

<p>Hi <%= @task.executor.profile.first_name%>,</p>

<p><%= @current_user.profile.first_name %> <%= @current_user.profile.last_name %> just sent you a new task/favor.</p>

<p>You can check it out <%= link_to "here", user_task_url(@task.executor_id, @task.id) %>.</p>

<p>Cheers,<br />Faskyn Team</p>

contact_email.html.erb

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
   <p>You have received a message from the site's contact form, from <%= "#{ @name }, #{ @email}." %></p>
   <p><%= @comment %></p>
 </body>
</html>

user.rb

  has_one :profile, dependent: :destroy
  has_many :assigned_tasks, class_name: "Task", foreign_key: "assigner_id", dependent: :destroy
  has_many :executed_tasks, class_name: "Task", foreign_key: "executor_id", dependent: :destroy

error message from sidekiq log:

2015-09-28T12:48:33.600Z 20264 TID-outp8berw TaskcreatorWorker JID-a0816ab8d1881e81c58569d2 INFO: start
2015-09-28T12:48:33.633Z 20264 TID-outp8berw TaskcreatorWorker JID-a0816ab8d1881e81c58569d2 INFO: fail: 0.033 sec
2015-09-28T12:48:33.635Z 20264 TID-outp8berw WARN: {"class"=>"TaskcreatorWorker", "args"=>["{\"task_assigner_first_name\":\"Szilard\",\"task_assigner_last_name\":\"Hungarian\",\"task_executor_first_name\":\"Andrew\",\"task_executor_email\":\"[email protected]\",\"task_executor_id\":3,\"task_id\":82}", 5], "retry"=>true, "queue"=>"default", "jid"=>"a0816ab8d1881e81c58569d2", "created_at"=>1443439028.7889678, "enqueued_at"=>1443444513.5961752, "error_message"=>"wrong number of arguments (5 for 2)", "error_class"=>"ArgumentError", "failed_at"=>1443439028.8198888, "retry_count"=>8, "retried_at"=>1443444513.632468}
2015-09-28T12:48:33.636Z 20264 TID-outp8berw WARN: ArgumentError: wrong number of arguments (5 for 2)
2015-09-28T12:48:33.636Z 20264 TID-outp8berw WARN: /Users/Silo/Desktop/ruby_on_rails/faskyn/app/mailers/task_mailer.rb:3:in `task_created'


2015-09-28T16:17:07.015Z 23136 TID-ow4vpw9ik TaskcreatorWorker JID-6cb35b9b42bebdbf128746c0 INFO: start
2015-09-28T16:17:08.178Z 23136 TID-ow4vpw9ik TaskcreatorWorker JID-6cb35b9b42bebdbf128746c0 INFO: fail: 1.163 sec
2015-09-28T16:17:08.180Z 23136 TID-ow4vpw9ik WARN: {"class"=>"TaskcreatorWorker", "args"=>["{\"task_assigner_first_name\":\"Szilard\",\"task_assigner_last_name\":\"Hungarian\",\"task_executor_first_name\":\"Peter\",\"task_executor_email\":\"[email protected]\",\"task_executor_id\":2,\"task_id\":83}", 5], "retry"=>true, "queue"=>"default", "jid"=>"6cb35b9b42bebdbf128746c0", "created_at"=>1443456758.3800051, "enqueued_at"=>1443457026.97385, "error_message"=>"undefined method `assigner' for nil:NilClass", "error_class"=>"NoMethodError", "failed_at"=>1443456758.398172, "retry_count"=>3, "retried_at"=>1443457028.177601}
2015-09-28T16:17:08.180Z 23136 TID-ow4vpw9ik WARN: NoMethodError: undefined method `assigner' for nil:NilClass
2015-09-28T16:17:08.180Z 23136 TID-ow4vpw9ik WARN: /Users/Silo/Desktop/ruby_on_rails/faskyn/app/mailers/task_mailer.rb:4:in `task_created'

Solution

  • You're using a reference to your @task instance variable that is not set when coming from sidekiq. You need to initialize your @task variable in the mailer method itself. Depending on the desired outcome, it can be done in several ways.

    1. @task = Task.new

      Initialize a task and all the objects it needs (assigner and executor) and then give them the values passed in from your worker.

    2. @task = Task.find(task_id)

      This is a better solution since then you don't need to pass all of parameters through sidekiq. Your task is already saved in the database, so just pass the id through and use it to find the task. It will have all the required data already.

    tasks_controller.rb

        def create
          @user = current_user
          @task = Task.new(task_params)
          if @task.save
            TaskcreatorWorker.perform_async(@task.id, @user.id)
            flash[:success] = "Task saved!"
            redirect_to user_tasks_path(current_user)
          else
            render action: :new
          end
        end
    

    taskcreator_worker.rb

        class TaskcreatorWorker
          include Sidekiq::Worker
        
          def perform(task_id, user_id) # Removed count since you weren't using it. Add it back if needed
            TaskMailer.task_created(
              Task.find(task_id),
              User.find(user_id)
            ).deliver_now # deliver_now since we're already in a sidekiq worker
          end
        end
    

    task_mailer.rb

        class TaskMailer < ActionMailer::Base
          def task_created(task, user)
            # Initialize variables you use in your view
            @task = task
            @current_user = user
            mail(from: '[email protected]',
                 to: "#{task.executor.email}",
                 subject: "[Faskyn] New task/favor from "\
                          "#{task.assigner.profile.first_name} "\
                          "#{task.assigner.profile.last_name}"
            )
          end
        end