Skip to content
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

RFC: non-blocking behavior for next_result #807

Closed
wants to merge 3 commits into from

Conversation

osheroff
Copy link

We were a multi-statement query in our, and discovered that when queries other than the first query blocked, (SELECT 1; SELECT SLEEP(3)) the call to next_result became completely uninterruptible. This was fixed well enough by by wrapping the next_result call in rb_thread_call_without_gvl, but we subsequently found that the next_result call still wouldn't respond to Thread#raise, which we needed in order to perform some timeout logic.

Went down the rabbit hole further to find that the vio suite of functions will over-read a buffer, so mysql2's methodology of calling rb_wait_for_single_fd really won't work reliably for next_result -- reading the first result in the do_query path will trigger a read of the result of the results (if they're ready), meaning that we'll poll forever; ugly. I worked around this by directly calling vio_has_data, but in order to do that I had to include all of violite.h, and I can't say I'm particularly happy about it.

Anyway, we probably are simply backing off our using of multi-statement selects, if you wanted to merge this (or had better ideas about how to properly poll for results when the c client library had buffered stuff), great.

(note, PR targets 0.3 just because thats what we're on still).

Ben Osheroff added 3 commits November 17, 2016 05:52
when actually preforming a multi-statement select with any non-trivial
components, the `next_result` call will block the ruby interpreter.
We fix this in two ways:

First, we wrap up the actual mysql_next_result call in a "no-gvl"
call so that the interpreter can do other work while next_result is
happening.  Second, we borrow a method from do_query and spin in a
select() loop on the mysql socket until the query is ready.  A quick
source dive into the underlying mysql source shows that next_result()
falls into the same path as mysql_real_query (cli_read_query_result), so
this should be a safe method.
@sodabrew
Copy link
Collaborator

Wow! This'll take me a bit to read through, thanks for the thorough research and PR!

}
}

static struct timeval *get_read_timeout(VALUE self, struct timeval *tvp)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When this function returns it returns the pointer from the tvp argument?

@@ -563,23 +578,20 @@ static VALUE do_query(void *args) {
rb_raise(cMysql2Error, "read_timeout must be a positive integer, you passed %ld", sec);
}
tvp->tv_usec = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While you're in here, would you move the tv_usec = 0 to be directly below the tv_sec = sec line?

* the underlying mysql client library will have read both results into the buffer,
* defeating our attempt to poll() until ready. detect that case by peeking at the "vio" buffer.
*/
if ( !wrapper->client->net.vio->has_data(wrapper->client->net.vio) ) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is has_data the only method that required the violite.h headers?

int retval;

for(;;) {
retval = rb_wait_for_single_fd(fd, RB_WAITFD_IN, tvp);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was surprised, so I double-checked that rb_wait_for_single_fd does allow a NULL timeval argument. Could you add a comment to let the future coders know that NULL can get through from get_read_timeout and is a legal value for this function?

https://github.com/ruby/ruby/search?utf8=%E2%9C%93&q=rb_wait_for_single_fd

@@ -0,0 +1,280 @@
/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking more closely, it's a little worse than just defining the one has_data method, we're actually becoming aware of the st_vio structure in order to call one of its member function pointers. I'm concerned that this isn't part of the "public API" and would be subject to change across MySQL versions.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's talk through this one before I address the rest of the stuff. Yes, you're definitely right that this isn't part of the public api; I'm not particularly happy with the solution, but I looked for quite a bit and couldn't find any function, public or not, that would advise you about whether you had to block.

Having said that, the history of this structure is that it hasn't had a breaking change since (maybe) 2104 or (probably) 2012: https://github.com/mysql/mysql-server/commits/5.7/include/violite.h

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the other possibility here is to declare dependency on the two functions that the has_next pointer could point to, vio_buff_has_data or vio_ssl_has_data, but this would mean figuring out whether the underlying connection has negotiated an SSL connection or not, and it's not clear to me if we can figure that out.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, that's a great lead. Let's see if there's a way to determine the connection type. It might be nice to expose that information, too like Mysql2::Client#ssl_connection?

@sodabrew
Copy link
Collaborator

sodabrew commented Jan 3, 2017

A while back I thought about changing the multiple results API a bit to help support streaming on the 2nd - nth queries in a multiple statements, see #600 and let me know what you think of the idea proposed there.

@sodabrew
Copy link
Collaborator

I want to pick up work on this PR again soon, but I prefer to bring it into a next major (0.5.0) rather than making major changes to 0.3.x or even 0.4.x now. Would you be amenable to rebasing your branch to master?

(note, PR targets 0.3 just because thats what we're on still)

@sodabrew sodabrew added this to the 0.5.0 milestone Apr 26, 2017
@sodabrew sodabrew modified the milestones: 0.5.0, 0.6.0, 0.5.1 Mar 19, 2018
@sodabrew sodabrew modified the milestones: 0.5.1, 0.5.2 Apr 7, 2018
@sodabrew
Copy link
Collaborator

Closing in favor of rebase #962

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants