-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
Implement Context Interface #496
Comments
It would be great to auto-detect support for
Documentation for MySQL statement-level limit: https://dev.mysql.com/doc/refman/5.7/en/optimizer-hints.html#optimizer-hints-execution-time |
My personal feeling: I don't like such an implicit magic. |
What should we do to cancel query?
MySQL cli chose 1. But I don't like it because:
|
(2) is not great. In https://github.com/letsencrypt/boulder/ we had been using I think if we go the route of implementing manual It would probably be necessary to register two driver names for the two diverging dialects, |
I don't like implement MySQL and MariaDB specific feature. |
I know it.
I also -1 on this. We may have another behavior option in the future. |
Unfortunately the driver interface doesn't indicate when a connection pool is opening or closing. However, while it is more work, it certainly isn't impossible or too much work to open one control connection per (host, port, username, password) combination that remains open while at least one request transaction with that combination is open. |
It may work sometimes, but it may not work sometimes. I think the first and the default implementation of cancellation should be:
After that, I'm not against adding "super clever, excellent, wonderful cancel implementation". |
I'm sorry, it was wrong. https://dev.mysql.com/doc/refman/5.7/en/kill.html
But I still prefer simple implementation as default. |
Not wanting to go too much out on a limb, I asked @bradfitz what he thought on this issue (postgresql will have a similar issue to solve):
This count would be held per unique DSN. If you'd like I could write up a POC for the bookkeeping aspect. |
Could you give me an URL for this discussion? At least, this idea can't follow Even apart from ConnMaxLifetime, I think KILLer connection can't work with TCP loadbalancer like LVM. |
Wow, closing the connection isn't enough to cancel a query in MySQL? and there's no cancel packet at the wire level? If supporting multiple slave servers behind load balancer is a goal (which is probably a good goal), and there's no way to send a cancel on an existing connection and closing the connection doesn't abort a query... what options are there? This is kinda sad. |
AFAIK, Yes.
Combine closing connection and ( While it's not an ideal, I think it's practical enough for most applications. |
I think context cancellation is more common than deadlines. I think you should probably also implement the "KILL nnnnn" mechanism for cancels. I think single MySQL instances are way more popular than load-balanced read-only pools. (And killing a slow SELECT query is much less important) |
Why closing connection is not enough for cancellation? I meant "Implement cancellation by just closing connection" + "Use MAX_EXECUTION_TIME if killing slow query is important". |
Oh, sorry, I misunderstood. If closing the connection makes MySQL stop doing work, that's great. You could even use it for the deadlines too (which is to say: ignore deadlines entirely and just wait on |
No. MySQL doesn't stop query. But client can continue working without waiting result of query. |
Oh, okay, I'm back to being sad about MySQL.
That's not a great analogy. Servers can choose what to do when a TCP connection finishes. That's why Go has https://golang.org/pkg/net/http/#CloseNotifier so servers can stop expensive work if the client doesn't care anymore. |
But this is a client. Go's http client can't kill server side Apache CGI process. It's desirable, but not required feature for clients, isn't it? I'm not against adding an option like |
You could offer an op-out in the DSN. No, it not required to actually cancel the connection. For the same reason that go is different than PHP, I think it is a good thing to have. EDIT: To be clear, there are many apps written in PHP and many uses of database drivers without cancelation. I write in Go because I think I can write better apps in it. I recommend implementing cancel because I think we can write better apps with it. |
It is my understanding both MySQL and Postgresql require using a different connection to cancel a currently running query, but we don't want to exceed any given connection pool limit. I propose having some method where the driver connection itself can request another connection from the database pool for such cancelation needs. This would ensure any limit the user set the pool did not exceed. This would also remove any need to match on DSN and store a single open connection somewhere in the driver. Maybe something like: package driver
// ConnRequester may be implemented by Conn. It should store the provided
// function until the connection is closed. This is intended to be used when canceling
// queries for databases that require another connection to be opened.
//
// The reqConnSameDSN returns a driver connection using the same DSN and a function
// that returns the connection to the connection pool.
type ConnRequester interface {
Conn
ConnRequest(req ReqConnSameDSN)
}
type ReqConnSameDSN func(ctx context.Context) (c Conn, returnConn func(error), err error)
package mysql
func (c *MySqlConn) ConnRequest(req driver.ReqConnSameDSN) {
c.req = req
}
func (c *MySqlConn) runCancel(f func(*MySqlConn) error) error {
if c.req == nil {
return errNoCancel
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) // TODO: Get from config.
defer cancel()
cancelerConn, returnConn, err := c.req(ctx)
if err != nil {
return err
}
defer returnConn(err)
mysqlCancelerConn := (*MySqlConn)(cancelerConn)
err = f(mysqlCancelerConn)
return err
} Please bear in mind the above is just a sketch. Do you think something like this would enable a simpler implementation of cancelation? |
For reference, here is how lib/pq implemented cancelation: https://github.com/lib/pq/pull/535/files . |
Could that behavior be triggered only when you do use Contexts ? Someone using Context clearly wants to be able to shutdown work, and so killing the remote process is something that'll be expected.. especially since other implementations tend to do the same (and it's the culture with contexts to try to kill as much as possible upon cancellation). |
Hm, It seems main goroutine waiting reply regardless context. On the other hand, it's pros is, additional goroutine is used only when cancellation happens. |
@kardianos Well regardless of any synchronization solution this is clearly a responsibility of database/sql and not the Driver (A Driver provides support for "query execution" not "query execution order" or manging dependency between arbitrary queries) Off the top of my head, I would expect database/sql not to reuse any Connection that has a Context attached to it until the Driver releases it, but I would need to think about some more in the context of transactions.. |
@PublicForest I don't understand what you are saying. The database pool won't re-use a connection until the driver is done with it. You were talking about a race condition where a cancellation is in flight while it is being returned. |
@kardianos I didn't say anything about connection pool. "re-use" as in execute a successor query on the same connection which has an in-flight Cancellation Go Routine for a predecessor query. |
When the driver query function returns, it is assumed the connection may be re-used. |
After we send "KILL QUERY", we shouldn't reuse the connection. MySQL provides connection id (thread id, unique in one server, not unique in cluster), |
@kardianos So perhaps there is a need for a unique ID (incrementing int64 counter) for each Context that will be stored within the Connection for the duration of the Query as well as in the Cancellation Go Routine so it can check if it needs to discard the cancellation. I would like to see such a counter maintained by database/sql per DB since it provides the Context to begin with and Connections are scoped to DB, but the Driver can also maintain that on a global level. |
@PublicForest your last few comments haven't made sense to me. |
@kardianos Sorry to hear that.. If you be specific on what you don’t understand I will try to clarify. |
So it turns out that database/sql will run its own Go routine to observe the user supplied Context. |
@PublicForest You aren't reading the database/sql code entirely correct. |
@kardianos Quite possibly, but thats where the debugger breaks when a WithTimout Context expires...Can you please clarify? |
@PublicForest Ensure you have looked at the tests that use context in database/sql. https://golang.org/src/database/sql/sql_test.go#L285 is a good place to start reading. Know the difference from the docs between a context provided to the BeginTx and QueryContext. |
@kardianos Don’t get it. Upon timeout nothing prevents awaitDone() go routine to make progress and issue a rollback on an already busy connection... There is even no guarantee that the canceling go routine will block on the Done channel while a long-standing query runs, let alone execute the cancellation code before the rollback call. In any case, why is database/sql even attempting to rollback the TX when the context expires? this is something DB servers do implicitly for START/BEGIN TRAN– COMMIT blocks when receiving cancellation request.. |
@PublicForest What is your motivation for commenting on this issue? Do you plan to send a PR to this driver? I'm not seeing any other pull request or code repo on your profile, so I'm slightly confused. Most of your questions can be answered in the package documentation. |
@kardianos My motivation was to help out in designing a solution for this. |
@PublicForest Sorry, I can't understand your English. Please use simple and more descriptive sentence. Example code is helpful too. |
Just found a problem in my testing of transactions and context.
The To demo the condition with high server load, I add sleep 3 sec to packets.go#L742 The test code above would panic, because the goroutine(in database/sql) which runs tx.awaitDone() just close the mysqlConn which rows uses, causing the panic. Sorry if this problem is already reported or known issue but I can't find proper solution except I do some invasive modification to both database/sql and go-sql-driver/mysql code. I guess the rows type and Tx need some sort of synchronization, such as lock driverConn. Any help would be really appreciated, thanks a lot! |
Can you show the panic stack? |
@kardianos Here's panic stack:
|
What about using net.Conn.SetDeadline(time.Now()) in case if need to interrupt current request. func (mc *mysqlConn) cancel() {
mc.netConn.SetDeadline(time.Now())
} there is still problem with orphan queries on mysql.server, but client will interrupt any query as soon as cancel on context is called. there is POC https://github.com/bgaifullin/mysql/tree/support_context2 |
Fixed by #608 |
See golang/go#15123
The text was updated successfully, but these errors were encountered: