-
Notifications
You must be signed in to change notification settings - Fork 546
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
stmt.execute cause Error: 2014 (CR_COMMANDS_OUT_OF_SYNC) #694
Comments
There is a limit to the number of prepared statements, perhaps you're hitting it? |
No. If adding
|
https://dev.mysql.com/doc/refman/5.6/en/commands-out-of-sync.html:
|
Interesting, ok. I'll see what I can figure out. Thank you for testing the 0.4.x releases more heavily, btw! |
I don't see any exceptions with a loop of <= ~1650. Race conditions? |
I can reproduce the error with the given script. If you close the statement handle on each loop, it works fine. I agree it would be best to provide some kind of better warning or cleanup to prevent this, but I'm not sure what that would be... 10000.times do |n|
stmt = client.prepare('SELECT * FROM mysql2_test LIMIT 1')
stmt.execute
stmt.close # adding this line lets it run all the way through
end |
Replacing |
Oh that makes sense actually! Ruby GC might be calling for a statement deallocate while the connection is in an incompatible state, such as being in the middle of returning a result set. Some printf or wire shark debugging might help identify just which commands are getting mixed up.
|
Yep, it's totally that Ruby GC is getting in the middle. With this loop, the order of mysql_stmt_foo calls is: 10000.times do |n|
stmt = client.prepare('SELECT * FROM mysql2_test LIMIT 1')
stmt.execute
GC.start
print '.'
end
Without the
Crucially, So... bottom line, I think that we need to ensure that GC doesn't run between mysql_stmt_execute and mysql_stmt_store_result. Turns out there is a way to do this! http://ruby-doc.org/core-1.8.7/GC.html#method-c-disable I'll link a test PR shortly. |
@kamipo I was testing this a bit more, and found that this loop runs well: 10000.times do |n|
num = n
stmt = client.prepare('SELECT * FROM mysql2_test LIMIT 1')
stmt.execute
stmt.close
end The proximate cause of failure being a GC of the stmt handle happening mid-way through the next loop, and so Would it be possible in your ActiveRecord work to make sure that statement handles are explicitly closed when they are removed from the handle cache? Edit: apparently I re-discovered my own findings from #694 (comment) gah. |
After reading through rails/rails#22415 it looks like the statement handles are always being freed. Maybe I should revisit your proposed Result.free from #692 ? That would also simplify the differences between mysql and mysql2 drivers, e.g. in AR the implementation for |
Prepared statements in mysql2 still have GC issue. brianmario/mysql2#694
Resolved by #729 - I'll add to the README that it is highly recommended to free results when using prepared statements. |
This issue is not resolved by #729. #729 can not prevent GC between require 'mysql2'
client = Mysql2::Client.new(
host: 'localhost',
username: 'root',
database: 'mysql',
)
begin
num = nil
3000.times do |n|
num = n
stmt = client.prepare('SELECT * FROM user LIMIT 1')
result = stmt.execute
result.free # added by https://github.com/brianmario/mysql2/pull/729
end
rescue
p num
p $! # <Mysql2::Error: Commands out of sync; you can't run this command now>
end Result:
|
Oh, the statement handle can still get garbage-collected out of order. For the Rails use case, since you are holding the references to the statement handles until you close them explicitly, is it sufficient simply to add For the general use case, as you show above, it sounds like the only solution is to hold the GVL? I would really hope to find an alternative if possible, to allow parallel execution of statements (i.e. other Ruby threads can continue locally while the MySQL server is processing a query). |
The text was updated successfully, but these errors were encountered: