Getting random nil.each error in mysql2 gem when using :include in Rails's find - mysql

I'm randomly (as it seems) getting a nil.each error on my localhost. Was feeling pretty confident in the code though so I pushed it to heroku and it works fine there.
Here's my view code:
<h2><%=#book.name.upcase%></h2>
<br />
<% #book.chapters.each do |chapter| %>
<h3>Chapter <%=chapter.number%></h3>
<% chapter.verses.each do |verse| %>
<b><%=verse.number%>)</b> <%=verse.body%>
<% end %>
<br /><br />
<% end %>
At first all I had in my controller was something like #book = Book.find(params[:id]) and that worked fine except for the speed. Then I changed to this:
def show
if params[:book_name]
##book = Book.find_by_sql(["select * from books where UPPER(name) = UPPER(?)", params[:book_name]]).first
#book = Book.where(["UPPER(name) = UPPER(?)", params[:book_name]]).includes(:chapters => :verses).first
raise ActiveRecord::RecordNotFound, "Page not found" if #book.nil?
elsif params[:id]
#book = Book.find(params[:id].to_i, :include => {:chapters => :verses})
end
end
Now I randomly get this error:
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.each
...
app/controllers/books_controller.rb:14:in `show'
One difference is heroku uses postgre and I'm using mysql.
Heroku url: http://kjv-on-rails.heroku.com/books/1
Full application code: https://github.com/tybro0103/KJV-on-Rails
Update
I just checked the Full Trace and realized the error occurs here:
mysql2 (0.2.6)
lib/active_record/connection_adapters/mysql2_adapter.rb:635:in
`select'
So the error is happening in the mysql2 gem.
Note
It's been brought to my attention that I have odd/redundant relations & data. I realize this, I promise! I don't believe that's causing the error though. :)
Note
By random here's what I mean... On some books I never see this error. On others I see 90% of the time, while yet on others I see it 50% of the time. I just hit refresh over and over and sometimes it works and sometimes it doesn't. I guess the only way to really see what I mean is to download the code yourself. My guess is no one will which means I'll start a bounty in a couple days as an incentive. :)
UPDATE
So the error occurs in the mysql2 adapter. Funny thing is that #book = Book.find(params[:id].to_i, :include => {:chapters => :verses}) causes the error when running rails server. In the rails console I can run the same command all day long and it works fine. It was using webrick, but I tried switching to mongrel and got the same error. I've also tried switching from ruby 1.8.7 to 1.9.2 and get the same error. It seems like maybe there is some sort of memory limit within the rails server maybe?

AHHHH... I found the trouble maker! I just downgraded mysql from 5.5 to 5.1 ...problem gone! What a pain in the #$$ that was.
It still would be nice to be able to use the latest release of mysql however. If someone can find a way to make it work using mysql 5.5 I'll mark that as the accepted answer.

Related

Activerecord error: Mysql2::Error: MySQL server has gone away

i've looked into previous questions which look similar to my problem, nothing seems to be related.
After doing the following query:
Runner::Models::Job.where(:id => job_id)
I get an exception, it happens once in a while:
Mysql2::Error: MySQL server has gone away: SELECT jobs.* FROM jobs WHERE jobs.id = 7507 LIMIT 1
I use puma, and in config.ru i am using:
use ActiveRecord::ConnectionAdapters::ConnectionManagement
The thing is that this error always happens in the same place, and i cant figure out why is it happening, i've checked memory, cpu, etc'.., all seem normal.
Maybe someone will have an idea.
thanks !
Write your Resque task like this:
class MyTask
def self.perform
ActiveRecord::Base.verify_active_connections!
# rest of your code
end
end
see more details here: https://github.com/resque/resque/wiki/FAQ#how-do-you-work-around-the-mysql-server-has-gone-away-error-

How does Rails build a MySQL statement?

I have the following code that run on heroku inside a controller that intermittently fails. It's a no-brainer that it should work to me, but I must be missing something.
#artist = Artist.find(params[:artist_id])
The parameters hash looks like this:
{"utf8"=>"������",
"authenticity_token"=>"XXXXXXXXXXXXXXX",
"password"=>"[FILTERED]",
"commit"=>"Download",
"action"=>"show",
"controller"=>"albums",
"artist_id"=>"62",
"id"=>"157"}
The error I get looks like this:
ActiveRecord::StatementInvalid: Mysql::Error: : SELECT `artists`.* FROM `artists` WHERE `artists`.`id` = ? LIMIT 1
notice the WHEREartists.id= ? part of the statement? It's trying to find an ID of QUESTION MARK. Meaning Rails is not passing in the params[:artist_id] which is obviously in the params hash. I'm at complete loss.
I get the same error on different pages trying to select the record in a similar fashion.
My environment: Cedar Stack on Heroku (this only happens on Heroku), Ruby 1.9.3, Rails 3.2.8, files being hosted on Amazon S3 (though I doubt it matters), using the mysql gem (not mysql2, which doesn't work at all), ClearDB MySQL database.
Here's the full trace.
Any help would be tremendously appreciated.
try sql?
If it's just this one statement, and it's causing production problems, can you omit the query generator just for now? In other words, for very short term, just write the SQL yourself. This will buy you a bit of time.
# All on one line:
Artist.find_by_sql
"SELECT `artists`.* FROM `artists`
WHERE `artists`.`id` = #{params[:artist_id].to_i} LIMIT 1"
ARel/MySQL explain?
Rails can help explain what MySQL is trying to do:
Artist.find(params[:artist_id]).explain
http://weblog.rubyonrails.org/2011/12/6/what-s-new-in-edge-rails-explain/
Perhaps you can discover some kind of difference between the queries that are succeeding vs. failing, such as how the explain uses indexes or optimizations.
mysql2 gem?
Can you try changing from the mysql gem to the mysql2 gem? What failure do you get when you switch to the mysql2 gem?
volatility?
Perhaps there's something else changing the params hash on the fly, so you see it when you print it, but it's changed by the time the query runs?
Try assigning the variable as soon as you receive the params:
artist_id = params[:artist_id]
... whatever code here...
#artist = Artist.find(artist_id)
not the params hash?
You wrote "Meaning Rails is not passing in the params[:artist_id] which is obviously in the params hash." I don't think that's the problem-- I expect that you're seeing this because Rails is using the "?" as a placeholder for a prepared statement.
To find out, run the commands suggested by #Mori and compare them; they should be the same.
Article.find(42).to_sql
Article.find(params[:artist_id]).to_sql
prepared statements?
Could be a prepared statement cache problem, when the query is actually executed.
Here's the code that is failing-- and there's a big fat warning.
begin
stmt.execute(*binds.map { |col, val| type_cast(val, col) })
rescue Mysql::Error => e
# Older versions of MySQL leave the prepared statement in a bad
# place when an error occurs. To support older mysql versions, we
# need to close the statement and delete the statement from the
# cache.
stmt.close
#statements.delete sql
raise e
end
Try configuring your database to turn off prepared statements, to see if that makes a difference.
In your ./config/database.yml file:
production:
adapter: mysql
prepared_statements: false
...
bugs with prepared statements?
There may be a problem with Rails ignoring this setting. If you want to know a lot more about it, see this discussion and bug fix by Jeremey Cole and Aaron: https://github.com/rails/rails/pull/7042
Heroku may ignore the setting. Here's a way you can try overriding Heroku by patching the prepared_statements setup: https://github.com/rails/rails/issues/5297
remove the query cache?
Try removing the ActiveRecord QueryCache to see if that makes a difference:
config.middleware.delete ActiveRecord::QueryCache
http://edgeguides.rubyonrails.org/configuring.html#configuring-middle
try postgres?
If you can try Postgres, that could clear it up too. That may not be a long term solution for you, but it would isolate the problem to MySQL.
The MySQL statement is obviously wrong, but the Ruby code you mentioned would not produce it. Something is wrong here, either you use a different Ruby code (maybe one from a before_filter) or pass a different parameter (like params[:artist_id] = "?"). Looks like you use nested resources, something like Artist has_many :albums. Maybe the #artist variable is not initialized correctly in the previous action, so that params[:artist_id] has not the right value?

Bind parameters in Rails mySQL empty

Using Rails 3.1.1, I'm getting occasional errors in production where it seems like the bind parameters on a mysql query are not there for some reason. The error looks like this:
A ActiveRecord::StatementInvalid occurred in events#show:
Mysql::Error: : SELECT `events`.* FROM `events` WHERE `events`.`id` = ? LIMIT 1
activerecord (3.1.1) lib/active_record/connection_adapters/mysql_adapter.rb:890:in `execute
It's not consistent on any insert or select, so I'm having trouble tracking it down. Does anybody have any suggestions?
Edit: updated with simpler example.
#events_controller.rb
def show
#event = Event.find(params[:id])
...
end
#called with parameters: {"action"=>"show", "controller"=>"events", "id"=>"26"}
The probable reason
Check your database driver installation here.
This seems to have gone away after upgrading to the latest rails - uncertain as to what it was.

fetching multiple result sets from mysql stored procedure in rails

I've been searching all over for tips on this and have not really had any luck so far. With the mysql2 gem, trying to execute a stored procedure that returns multiple result sets gives me an unable to return results in this context error. I found someone had suggested to use the mysql gem instead (which I can't find an explanation of what's different between the two and what I might encounter by switching), and with that I've had more progress.
Here's what I have so far:
>> db = ActiveRecord::Base.connection.raw_connection
=> #<Mysql:0x1056ae3d8>
>> ActiveRecord::Base.connection.select_all("CALL p_rpt_test('', '');")
=> [{"Header"=>"Client,Project,Type,Due Date,Assigned To"}]
>> db.more_results?
=> true
>> db.next_result
Mysql::Error: Commands out of sync; you can't run this command now
from (irb):3:in `next_result'
from (irb):3
Does anyone know of a way to get this to work, with mysql2 or mysql gems? The app is running rails 3.0.1.
Ok well I have no figured out how to get AR to do this so I've ended up just going low level and using the mysql driver itself, which mostly works...
data = Array.new
db = ActiveRecord::Base.connection.raw_connection
header = db.query("CALL #{self.proc}(#{args});")
header.each {|r| data << r}
if db.next_result
rows = db.store_result
rows.each {|r| data << r}
end
ActiveRecord::Base.connection.reconnect!
It works, but I can't imagine there's not a better way. Also I have to reconnect after this or I get an error on the next query, and I haven't found a way to properly close the session. Oh and I have to use the mysql gem and not mysql2.
Grrrrr.
We can use header.to_hash to get an array of hash, or header.rows to get an array of array.
Follow this http://api.rubyonrails.org/classes/ActiveRecord/Result.html

asp mysql variables testing problem

I have a problem that just started happening after I reinstalled my website's server.
In the past I could do do this:
Code:
<%
set msgSet = conn.execute("select * from base_scroller where scroller_num = 1"
%>
check if it's not empty or anything else
Code:
<% if msgSet("scroller_name") <> "" then %>
and if it is i could do anything with it (like showing it's value)
Code:
<%= msgSet("scroller_name") %>
<% end if %>
Now I can't do this, the "if" test doesn't work with the "msgSet("scroller_name")" and I have to redifine it first in another variable
Code:
<% scrollername = msgSet("scroller_name") %>
then and only then I can do tests on it...
Code:
<% if scrollername <> "" then %>
and show it too.
<%= scrollername %>
<% end if %>
I would just like to get back the option to do the operations on the mysql recordset variables like b4....
Has someone come across this problem ? what has changed, is it a falty mysql varsion or something ?
Thank you guys.
There are two things you should do to ensure you have a value in a field:
Make sure the recordset is not empty.
Make sure the field in the current does not have a NULL value.
I am not away of any changes in the drivers that have affected your code, but I assume the difference is that your string actually returns an empty string (which would equal "") and your recordset is returning a proper NULL value (which is not equal "")
More details:
my odbc is: mysql 5.1 driver.
my mysql version : mysql server 5.0
my connetion string is:
I've tried to remove the STMT=SET CHARACTER SET hebrew;OPTION=3; part - no change...
the problem is using any variables right from the db, it could be any kind of variable (text,date,int)...
even
day(msgSet("scroller_date")) doesn't work now...
and th funny this is... it all used to work just fine.. b4 the installation
you see anything out of the ordinary ? maybe a different mysql/ODBC version ?
Ok...
the new odbc is 5.1 : located on the mysql.com servers (link: http://dev.mysql.com/downloads/connector/odbc/5.1.html)
Yes. I've reinstalled the OS (windows server 2003) again on my webserver and installed mysql server 5.0 on it.
I'm not getting an error,it is just not returning any data when I use the method I was explaining abourt, and I have to use the extra variables like I explained.
Do you require anymore details please ?
It could be a better way to test to check to see if the recordset is empty in an alternative manner.
I generally use:
On Error Goto 0
set msgSet = conn.execute("select * from base_scroller where scroller_num = 1"
If msgSet.EOF = True And msgSet.BOF = True Then
Response.Write "Recordset cursor was at the beginning and end of file - empty set."
Response.End
End If
This might help some way towards debugging it.
Oh, and something that might be important. I can't remember what caused it, but sometimes I found that when referencing MySQL fields via a recordset, it was always lower case, regardless of what the fields are in the database definition or query.
If this doesn't cause your script to fail, maybe try finding out what has been returned by the recordset.
Dim i
For Each i In msgSet.Fields
Response.Write i & "=[" & msgSet.Fields(i) & "]<br />"
Next
Good luck
Never seen that problem before, nor am I sure why (more information on the error would be helpful) - but, as a relatively quick fix you may want to cast the item when you use it. Like:
<%= cStr(msgSet("scroller_name")) %>
See http://www.w3schools.com/vbscript/vbscript_ref_functions.asp#conversion for more information.
More information about the error you are getting would help you get a better answer. There is no MyODBC 5.1, so which version of the MyODBC drivers are you using? By "my odbc is: mysql 5.1 driver" you mean the driver for MySQL 5.1 - that should be MyODBC version 3.51.
Also, by "after I reinstalled my website's server" do you mean you did a clean install of the server OS? or did you just re-install MySQL? Or something else? MySQL 5.1 just came out 8 Dec. - was this part of an upgrade?
Lastly, please read some of the comments to your question and some of your replies. All things being equal, if there is some clarification needed, edit the question to add the details. Don't write a response. This isn't a forum, and you're response will lose its context the minute something is up-voted above it. Also its easier to take in the entire issue if its all in one spot as opposed to scrolling between the question and the various addendums posing as replies.
I'm getting the same problem...
Just migrating an asp site from mysql 4 & odbc 3.x (an old version...) to mysql 5.1 and odbc 5.1 .
If i try this simple code :
set rs = conn.execute("select ....")
while not rs.eof
response.write "t1 : " & rs("text") & "t2 : " & rs("text") & ""
rs.movenext
wend
as output i get the following :
t1 : hello
t2 :
t1 : how are you
t2 :
etc...
The second time I access the field it has no value, the only way is to use a temp variable to store the data the first time...