You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ActiveRecord find_by在数据库有有效记录时返回nil问题求助

Action Cable 中 find_by_name 偶尔返回 nil 的问题排查与解决方案

我们生产环境使用Action Cable时,通道中的article = Article.find_by_name("cat")代码偶尔失效——数据库明明存在有效记录,却返回nil。推测是竞态条件导致,但生产日志和MySQL日志无异常。

使用版本

  • rails: 5.2.3
  • ActiveRecord: 5.2.3
  • mysql2: 0.5.2
  • actioncable: 5.2.3

database.yml 配置

default: &default
  adapter: mysql2
  encoding: utf8
  reconnect: true
  pool: 5
  host: localhost
  username: root
  password: root

development:
  <<: *default
  database:  localdb 
  
test:
  <<: *default
  database: xipk_test

production:
  <<: *default
  host: <%= ENV['Host'] %>
  database: <%= ENV['database'] %>
  username: <%= ENV['username'] %>
  password: <%= ENV['password'] %>

Article 通道代码

class ArticleChannel < ApplicationCable::Channel

  def subscribed    
    stream_from "article_#{uuid}_channel"
    name = params["article[name]"]
    article = Article.find_by_name(name)
    if !article.nil?
      response_code = 404
    else
      response_code = 200
    end
    ActionCable.server.broadcast "article_#{uuid}_channel", {
      article_response: article_response,
      response_code: "#{response_code}"
    }
  end

end

Article 模型代码

class Article < ApplicationRecord

  validates_presence_of :name
  validates_uniqueness_of :name, scope: :title, :case_sensitive=> false
  validates_presence_of :address
  belongs_to :user, optional: true
  has_many :request_histories, :dependent => :destroy
  default_scope { order('updated_at DESC') }
  before_create :generate_guid, unless: :guid?
  after_save :update_dashboard
  

  def self.default_scope
    where("is_test = ? AND (test_mode = ?  OR test_mode IS NULL)", false, "ABB")
  end

  def generate_guid
    self.guid ||= SecureRandom.uuid
  end


  def check_witl_null_value(c_val)
    act_val = self.send("#{c_val}")
    act_val if act_val!= "null"
  end 

end

排查与解决方案

1. 修复重复定义的默认作用域

模型中重复定义了default_scope,Rails会叠加执行这些条件,可能意外过滤掉符合条件的记录。合并为单一默认作用域:

class Article < ApplicationRecord
  # 合并默认作用域,避免条件冲突
  default_scope { where("is_test = ? AND (test_mode = ? OR test_mode IS NULL)", false, "ABB").order(updated_at: :desc) }
  
  # 其他代码保持不变
end

2. 解决竞态条件下的事务隔离问题

Action Cable运行在独立线程池,MySQL默认的REPEATABLE READ隔离级别可能导致线程读取到旧数据快照。查询时强制使用READ COMMITTED隔离级别:

# 在通道查询中添加事务隔离控制
article = Article.transaction(isolation: :read_committed) do
  Article.find_by_name(name)
end

3. 修正大小写敏感的查询逻辑

模型中唯一性约束是大小写不敏感的,但find_by_name默认大小写敏感。修改查询为不区分大小写:

# 适配MySQL的不区分大小写查询
article = Article.where("LOWER(name) = LOWER(?)", name).first

4. 确保数据库连接有效性

Action Cable线程池可能存在连接失效未重连的情况,查询前验证连接:

# 先验证数据库连接状态
ActiveRecord::Base.connection.verify!
article = Article.find_by_name(name)

5. 修复通道代码的逻辑错误

通道中存在两处逻辑错误:

  • 存在记录时返回404,逻辑完全颠倒
  • article_response变量未定义

修正后代码:

def subscribed    
  stream_from "article_#{uuid}_channel"
  name = params["article[name]"]
  article = Article.find_by_name(name)
  
  if article.nil?
    response_code = 404
    article_response = nil
  else
    response_code = 200
    article_response = article.as_json # 按需构造响应数据
  end
  
  ActionCable.server.broadcast "article_#{uuid}_channel", {
    article_response: article_response,
    response_code: "#{response_code}"
  }
end

内容的提问来源于stack exchange,提问作者Bala

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 23:09:34