0

I'm building text message system that will save to sqlite, the error im getting is from the code. on creating new message I get this error

ActiveRecord::StatementInvalid (SQLite3::BusyException: database is locked: INSERT INTO "notifications" ("content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)):
ActiveRecord::StatementInvalid (SQLite3::BusyException: database is locked: commit transaction): 

if i remove this line

ActionCable.server.broadcast "conversation_#{@conversation.id}", message: render_message(@message)

from app/controllers/messages_controller.rb

class MessagesController < ApplicationController
    before_action :authenticate_user!
    before_action :set_conversation
def index
    if current_user == @conversation.sender || current_user == @conversation.recipient
    @other = current_user == @conversation.sender ? @conversation.recipient : @conversation.sender
    @messages = @conversation.messages.order("created_at DESC")
    else
    redirect_to conversations_path, alert: "You don't have permission to view this."
    end
end
def create
    @message = @conversation.messages.new(message_params)
    @messages = @conversation.messages.order("created_at DESC")
if @message.save
   ActionCable.server.broadcast "conversation_#{@conversation.id}", message: render_message(@message)
   redirect_to conversation_messages_path(@conversation)
   end
 end
 private
 def render_message(message)
    self.render(partial: 'messages/message', locals: {message: message})
end
def set_conversation
   @conversation = Conversation.find(params[:conversation_id])
end
  def message_params
   params.require(:message).permit(:context, :user_id)
  end
end

and i need to remove this part , remote: true from this line

<%= form_for [@conversation, @conversation.messages.new], remote: true do |f| %>

from app/views/messages/index.html.erb

  <div class="panel-body">
    <div class="container text-center">
      <%= form_for [@conversation, @conversation.messages.new], remote: true do |f| %>
       <div class="form-group">
          <%= f.text_field :context, placeholder: "Add a personal message", class: "form-control" %>
          </div>
          <%= f.hidden_field :user_id, value: current_user.id %>
          <div>
            <%= f.submit "Send Message", class: "btn btn-normal" %>
          </div>
      <% end %>
    </div>
  </div>

if i removed the top 2 lines The message will work and save to the database but i wont have the real time update on the receiver side and here are some other parts of my code

app/assets/javascripts/channels/messages.coffee

$(() ->
     App.messages = App.cable.subscriptions.create {channel: 'MessagesChannel', id: $('#conversation_id').val() },
    received: (data) ->
    $('#new_message')[0].reset()
    $('#chat').prepend data.message
 )

app/channels/messages_channel.rb

class MessagesChannel < ApplicationCable::Channel
  def subscribed
  stream_from "conversation_#{params[:id]}"
  end
end

lastly this one app/views/conversations/index.html.erb

     <div class="container">
      <% @conversations.each do |conversation| %>
          <% other = current_user == conversation.sender ? conversation.recipient : conversation.sender %>
          <%= link_to conversation_messages_path(conversation) do %>
              <div class="row conversation">
                <% if conversation.messages.any? %>
                    <div class="col-md-2">
                      <%= image_tag avatar_url(other), class: "img-circle avatar-medium" %>
                    </div>
                    <div class="col-md-2">
                      <%= other.fullname %>
                      <%= conversation.messages.last.message_time %>
                    </div>
                    <div class="col-md-8">
                      <%= conversation.messages.last.context %>
                    </div>
                <% end %>
              </div>
          <% end %>
      <% end %>
    </div>

here is config/database.yml

default: &default
   adapter: sqlite3
   pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 30 } %>
   timeout: 30000
development:
   <<: *default
   database: db/development.sqlite3
test:
   <<: *default
   database: db/test.sqlite3
production:
   <<: *default
   database: db/production.sqlite3
Amer Bearat
  • 876
  • 2
  • 7
  • 25
  • Error sounds like issue with concurrency and SQLite 3. See https://stackoverflow.com/questions/7154664/ruby-sqlite3busyexception-database-is-locked – lacostenycoder Mar 19 '18 at 01:51
  • No its not related to sqlite3 cause it will save in the database for me if i removed the 2 lines there and i wont get any errors @lacostenycoder – Amer Bearat Mar 19 '18 at 01:54
  • ActionCable should be run by background jobs and not inside your controller. Which "top 2 lines" ? – lacostenycoder Mar 19 '18 at 02:10
  • @lacostenycoder This line `ActionCable.server.broadcast "conversation_#{@conversation.id}", message: render_message(@message) ` – Amer Bearat Mar 19 '18 at 21:37
  • @lacostenycoder and this part **, remote: true** from this line `<%= form_for [@conversation, @conversation.messages.new], remote: true do |f| %> ` – Amer Bearat Mar 19 '18 at 21:39
  • Could you please add your config/database.yml file in your question? – Arthur Del Esposte Mar 20 '18 at 00:37
  • Try moving your `ActionCable.server.broacast` code to a model callback `:after_save`. Though db lock errors indicate race conditions errors. Can you test if you get the same errors if you switch to using postgres in the DB? I assume this app is in development only right? – lacostenycoder Mar 20 '18 at 02:32
  • Also in database.yml try increasing pool size `pool: 30` or something. Develop default I think is 5. – lacostenycoder Mar 20 '18 at 02:39
  • @lacostenycoder i update my question u should see my database.yml thank you for helping. I really appreciated. I did if message. after_save then i had my ActionCable.server.broadcast code and still got the same problem. – Amer Bearat Mar 20 '18 at 22:59
  • try to change `pool: 30 ` instead of `ENV` but also try with postgres instead of sqlite3 – lacostenycoder Mar 21 '18 at 01:55
  • 1
    @lacostenycoder I changed my database to mysql and now im not getting the error anymore it solved my problem, i did mysql cause im using aws as my server not Heroku again thank you for the help – Amer Bearat Mar 22 '18 at 17:18

1 Answers1

0

Sqlite3 has poor concurrency support so switch DB to a more robust backend either MySQL or Postgres.

lacostenycoder
  • 10,623
  • 4
  • 31
  • 48