sidekiqでの投稿がDBに保存されない
□解決したいこと
投稿に時間がかかるため、sidekiqで非同期処理をしたいと考えております。
投稿がデータベースに保存されないため、アドバイスをいただきたく、投稿いたしました。
sidekiq導入前のコントローラーの記述は以下でした
def create
@tip = TipTag.new(tip_params)
tag_list = params[:tip][:name].split(',')
if @tip.valid?
@tip.save(tag_list)
redirect_to root_path
else
render :new
end
end
保存に時間がかかるため、@tip.saveをsidekiqで非同期処理したいと思っておりました。
しかし、sidekiqを導入するよう記述を変更し、実行した際に、DBに投稿が保存されておりまでした。
投稿した際のsidekiqのログを確認すると一部にNoMethodErrorが現れましたので、当該部分がDBに保存されない原因かと想定しております。
以下、sidekiq導入のコードの内容
tips_controller.rb
def create
@tip = TipTag.new(tip_params)
@tag_list = params[:tip][:name].split(',')
if @tip.valid?
TestWorker.perform_async(@tip, @tag_list)
redirect_to root_path
else
render :new
end
end
test_worker.rb
class TestWorker < ApplicationController
include Sidekiq::Worker
def perform(tip, tag_list)
tip.save(tag_list)
end
end
siidekiq.yml
:verbose: false
:pidfile: ./tmp/pids/sidekiq.pid
:logfile: ./log/sidekiq.log
:concurrency: 10
:queues:
- default
- test
tiptag.rb
class TipTag
include ActiveModel::Model
attr_accessor :title, :category_id, :description, :user_id, :image, :name, :id, :_method, :authenticity_token, :commit, :tip
validates :category_id, numericality: { other_than: 1 }
with_options presence: true do
validates :title, :description, presence: true
end
delegate :persisted?, to: :tip
def initialize(attributes = nil, tip: Tip.new)
@tip = tip
attributes ||= default_attributes
super(attributes)
end
def save(tag_list)
ActiveRecord::Base.transaction do
@tip.update(title: title, category_id: category_id, description: description, image: image, user_id: user_id)
@tip.tip_tag_relations.each do |tag|
tag.delete
end
tag_list.each do |tag_name|
tag = Tag.where(name: tag_name).first_or_initialize
tag.save
tip_tag = TipTagRelation.where(tip_id: @tip.id, tag_id: tag.id).first_or_initialize
tip_tag.update(tip_id: @tip.id, tag_id: tag.id)
end
end
end
def to_model
tip
end
private
def default_attributes
{
title: tip.title,
category_id: tip.category_id,
description: tip.description,
image: tip.image,
name: tip.tags.pluck(:name).join(','),
}
end
end
以下、エラーと想定される部分のログ
% bundle exec sidekiq -C config/sidekiq.yml
<省略>
2021-07-17T11:54:14.782Z pid=31588 tid=ovj3tn428 WARN: {"context":"Job raised exception","job":{"retry":true,"queue":"default","class":"TestWorker","args":["#<TipTag:0x00007fa3d945ce80>",[]],"jid":"a09eb53e3e3f2aa9c383daff","created_at":1626522854.658729,"enqueued_at":1626522854.659025},"jobstr":"{\"retry\":true,\"queue\":\"default\",\"class\":\"TestWorker\",\"args\":[\"#<TipTag:0x00007fa3d945ce80>\",[]],\"jid\":\"a09eb53e3e3f2aa9c383daff\",\"created_at\":1626522854.658729,\"enqueued_at\":1626522854.659025}"}
2021-07-17T11:54:14.782Z pid=31588 tid=ovj3tn428 WARN: NoMethodError: undefined method `save' for "#<TipTag:0x00007fa3d945ce80>":String
<省略>
□仮説及び調べたこと
tipのclassがstring型になっていることが問題ではないかと仮定しました。
そこでworker.rb内でbinding.pryを行い、値を取得しました。
参考
https://qiita.com/kosukeKK/items/1839d470e9472861fe6b
#以下ターミナル
4: def perform(tip, tag_list)
=> 5: binding.pry
6: tip.save(tag_list)
7: end
pry(#<TestWorker>)> tip.class
=> String
以上より、tipの型がStringになっております。
saveメソッドはString型には使用できないのが原因?ではないかと考えました。
そこで、tips_controllerに戻り、@tipがどのclassかを確認しました。
#以下ターミナル
15: def create
16: @tip = TipTag.new(tip_params)
17: @tag_list = params[:tip][:name].split(',')
=> 18: binding.pry
19: if @tip.valid?
[1] pry(#<TipsController>)> @tip.class
=> TipTag
[2] pry(#<TipsController>)> TipTag.class
=> Class
TipTagは中間モデルです。
TipTagはActiveModelです。
classはClass 型となりました。
参考
https://docs.ruby-lang.org/ja/latest/method/Object/i/class.html
□わからなかったこと
test_worker.rb内でtipのclassの型がなぜString型になったかがわかりませんでした。
初心者で、全く見当違いの検討をしているかもしれませんが、ご教示いただきたくお願い申し上げます。
□環境
ruby 2.6.5
rails (6.0.3.7)
sidekiq (6.2.1)
redis (4.3.1)