通过Sidekiq发送Devise确认邮件时遇用户映射及找不到用户问题
Devise + Sidekiq 发送确认邮件初期报错问题解决
问题描述
用户注册时,通过Sidekiq异步发送Devise确认邮件,初期持续出现Couldn't find User with 'id'=xxx错误(用户提到的"Could not find a valid mapping for User"为初期表象,实际日志显示记录未找到),但Sidekiq重试几分钟后邮件可正常发送。已按Devise文档重写send_devise_notification方法,尝试过after_commit触发仍未解决。
相关代码:
class User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :confirmable def send_devise_notification(notification, *args) devise_mailer.send(notification, self, *args).deliver_later end end
报错日志:
2025-01-28T14:52:31.012Z pid=30324 tid=6kk class=ActionMailer::MailDeliveryJob jid=3aba9b7903de8de2e2a5905e elapsed=0.024 INFO: fail 2025-01-28T14:52:31.012Z pid=30324 tid=6kk WARN: {"context":"Job raised exception","job":{"retry":true,"queue":"default","wrapped":"ActionMailer::MailDeliveryJob","args":[{"job_class":"ActionMailer::MailDeliveryJob","job_id":"96a7d608-0233-4a79-a7d5-f4d1f3c64e18","provider_job_id":null,"queue_name":"default","priority":null,"arguments":["Devise::Mailer","confirmation_instructions","deliver_now",{"args":[{"_aj_globalid":"gid://lookn-for-group/User/33"},"A8enbvseX9GdGGuY5YG8",{"_aj_symbol_keys":[]}],"_aj_ruby2_keywords":["args"]}],"executions":0,"exception_executions":{},"locale":"en","timezone":"UTC","enqueued_at":"2025-01-28T14:41:47.395153390Z","scheduled_at":"2025-01-28T14:41:52.394533706Z"}],"class":"ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper","jid":"3aba9b7903de8de2e2a5905e","created_at":1738075307.395294,"enqueued_at":1738075950.9874232,"error_message":"Error while trying to deserialize arguments: Couldn't find User with 'id'=33","error_class":"ActiveJob::DeserializationError","failed_at":1738075312.8300993,"retry_count":4,"retried_at":1738075647.2435138}} 2025-01-28T14:52:31.012Z pid=30324 tid=6kk WARN: ActiveJob::DeserializationError: Error while trying to deserialize arguments: Couldn't find User with 'id'=33 2025-01-28T14:52:31.014Z pid=30324 tid=6kk WARN:
解决方案
原因分析
报错核心是Sidekiq执行邮件任务时,用户记录尚未持久化到数据库,或数据未同步到Sidekiq连接的数据库节点:
- Devise注册流程在事务中执行,直接调用
deliver_later会导致任务在事务提交前入队,Sidekiq执行时用户记录还不存在; - 若使用数据库读写分离,Sidekiq连接从库时,新用户数据可能未完成主从同步。
方案1:确保事务提交后触发邮件任务
修改User模型的send_devise_notification方法,绑定到after_commit回调,仅在用户创建事务提交后异步发送邮件:
class User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :confirmable def send_devise_notification(notification, *args) # 针对确认邮件,仅在用户创建并提交事务后触发异步发送 if persisted? && notification == :confirmation_instructions after_commit -> { devise_mailer.send(notification, self, *args).deliver_later }, on: :create, prepend: true else # 其他通知沿用默认处理逻辑 super end end end
on: :create:限定仅在用户创建操作的事务提交后执行;prepend: true:确保该回调优先于其他可能的after_commit回调执行,避免被覆盖。
方案2:解决数据库读写分离同步延迟
若应用使用读写分离,配置Sidekiq强制连接主库,避免从库同步延迟问题:
在config/initializers/sidekiq.rb中添加以下配置:
Sidekiq.configure_server do |config| config.on(:startup) do # 让Sidekiq工作进程连接主库 ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[Rails.env]["primary"]) end end
- 注:若你的主库配置键不是
primary,请替换为实际配置名(比如master)。
内容的提问来源于stack exchange,提问作者David Bista
相关产品推荐
相关产品推荐

