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

Rails Action Cable实时消息报错:SQLite数据库锁定问题求助

Fixing SQLite3::BusyException When Using ActionCable Broadcast in Rails

This error pops up because SQLite relies on file-level locking, and your ActionCable broadcast is trying to access the database while the main request's transaction still holds a lock. ActionCable runs in a separate thread, so when you call ActionCable.server.broadcast inside your controller action (which Rails wraps in a transaction by default), the broadcast thread attempts to interact with the database before the original transaction commits—triggering the "database is locked" error.

Here are the most reliable fixes:

1. Move the Broadcast to an after_commit Callback

The cleanest solution is to trigger the broadcast after the database transaction is fully committed. This ensures the database lock is released before ActionCable tries to access it.

Add this to your Notification model (or whichever model you're creating):

after_commit :broadcast_to_conversation, on: :create

private

def broadcast_to_conversation
  # Use only persisted data here—no uncommitted records
  broadcast_data = {
    content: self.content,
    user_id: self.user_id,
    created_at: self.created_at.iso8601,
    # Include any other needed attributes
  }

  ActionCable.server.broadcast "conversation_#{self.conversation.id}", broadcast_data
end

Then remove the ActionCable.server.broadcast line from your controller. The callback will run automatically once the notification is saved and the transaction is finalized.

2. Pre-Serialize Data to Avoid Database Access During Broadcast

If you prefer to keep the broadcast in the controller, avoid passing ActiveRecord objects directly to the broadcast. Instead, extract all necessary data into a hash first, so the broadcast thread doesn't need to query the database again:

def create
  @notification = Notification.new(notification_params)

  if @notification.save
    # Pre-build all data needed for the broadcast
    broadcast_data = {
      content: @notification.content,
      user_id: @notification.user_id,
      created_at: @notification.created_at.iso8601,
      username: @notification.user.username # Fetch related data upfront if needed
    }

    # Broadcast the pre-serialized hash
    ActionCable.server.broadcast "conversation_#{@conversation.id}", broadcast_data
    # ... rest of your response logic
  else
    # Handle validation errors
  end
end

This way, the broadcast thread doesn't hit the database at all, eliminating lock conflicts.

3. Increase SQLite's Busy Timeout (Temporary Workaround)

For a quick fix while implementing the above solutions, you can tell SQLite to wait longer before throwing a locked error. Update your config/database.yml for the SQLite environment:

development:
  adapter: sqlite3
  database: db/development.sqlite3
  timeout: 5000 # Wait 5 seconds instead of the default 1 second

This won't solve the root issue, but it can reduce error frequency while you adjust your code.

Note: SQLite isn't ideal for multi-threaded applications like those using ActionCable. If you plan to scale your app, consider switching to a client-server database like PostgreSQL, which handles concurrent connections far better.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:08:12