-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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
database/sql: add Context support #15123
Comments
@pbnjay wrote on the parent bug:
"instead of"? Remember you can also query outside of transactions. |
I started implementing this a while back ago with having context as the first parameter to all the api functions. Apart from SetMaxIdleConns and SetMaxOpenConns all functionality implemented. Given the fact that my implementation breaks the whole api, implementation is purely done in a third party manner (without touching to std lib code) For the ref, you can find the docs here https://godoc.org/github.com/cihangir/ctxdb I would like to help on the implementation if need.
I tried to achieve this by closing the connection to the database Some of the database vendors handle it properly, but i am not sure if it is a general solution. |
You can query outside of transactions, but I think combining a user-space context with the database transaction (even for a single-query transaction) is troublesome. If you run an UPDATE but your context times out, user space indicates a cancellation/error, but the database may complete the request successfully. Sure this would be a user error, but ensuring that all Context usage occurs within a transaction allows for consistent results. Surely I'm missing something? |
I like @pbnjay's idea of having tying contexts into Tx -- even if we have to add a way to "begin" a fake transaction, or create an interface that With the "magic first argument" behavior, there are too many open questions. What happens if a user passes in a first argument that implements both Also, users may infer that multiple contexts may be passed in (whichever is cancelled first), or have subtle bugs that didn't occur before but can occur now, such as sorting the parameter I don't think we need a parallel API, but we can shift the proposal to a couple new methods that flow into the existing API (and don't sacrifice type-safety). |
FWIW, I have just implemented a context-oriented query canceler for MySQL. Not only in Go, but MySQL server should also be able to cancel running queries
This was possible because I knew my program issues only SELECT statements. From this experience, I'm not sure whether it is good or not that database drivers The code is here for your reference: |
This one may need https://gist.github.com/pbnjay/96f829f8aa77d7ae1d88bca094e5d6ec Since @ymmt2005 also mentioned mysql-specific commands, it leads me to think it can't be done from the current generic interface. |
Context support isn't a good fit for database/sql, there's no portable way to add deadlines or cancellation signals, I'd prefer to see a separate package like database/context that wraps *database.DB methods with checks to see if Done() is closed for a passed context and adds appropriate errors. |
With explicit transactions you're merely moving this problem from the UPDATE to the COMMIT command, not eliminating it. |
Seems very likely. |
Context is a great fit for sql. Almost all rdbms have a query cancel method and all databases need to ensure their connections are returned to the pool when the request is finished. One option I could see is in sql.DB add a In the driver you might need new interfaces that could be tested for, such as
Does that sound about right? |
I hope to start designing some CLs for testing. What do you think of adding a Ctx method to *sql.DB which returns a *sql.DB, but allows any queries from the returned DB to use the context. This would be done per request obviously. I want to make sure I'm not barking up the wrong tree on this. |
I think we have a convention of calling such a method |
@quentinmit I think with a connection pool, you may specify a connection timeout in the DSN. You then pass a context to the context pool with a timeout for acquiring the connection. You are correct about the method name. Agree it should be spelled |
I haven't used context everywhere it needs to be used, but the API and basic plumbing is present. Feedback on the approach welcome. |
@kardianos, remove the "DO NOT REVIEW" label from your CL if you want a review. |
CL https://golang.org/cl/28532 mentions this issue. |
@kardianos and I discussed this today. He's going to write up a summary of our discussion here. |
Current design adds the following methods to the
The goal is to drive a middle ground between having a context on every Rows method, but not on what is designed to be globals such as *DB or *Stmt nor store context in what looks like a *DB. The context parameter is designed to signal the driver to close the connection (cancelling the query if the driver supports it) and return the connection to the database pool at the end of the request. The
|
Off topic, but this new API catches my attention:
It would be better if we can also pass a flag to turn the transaction read-only. Any chance can BeginLevel take the third parameter as |
@ymmt2005 Thank you for pointing that out. I've done a rough survey of systems that support this. I'll need to do a more in depth survey before having an opinion, but so far I'm in favor of doing something for that. |
@noblehng I'm aware of what ISO defines is only 4 levels. But you didn't give an example of a different iso level, just that a particular existing isolation level may have different meaning. It think that is where we differ in opinion. Is it okay to have different database system take the same const when they might be implemented differently have have different properties? |
What about defining an interface for drivers to define supported transaction levels? |
@derekparker I don't understand what you mean by that. Could you give an example? |
@kardianos You can provide some predefined flags in the stdlib, I have no objection to that, but you will also need to add a big warning in its documentation. Database driver specific flags can also coexist with stdlib predefined flags. Use a |
@noblehng Can you give me an example of a name of a transaction level that is not represented in the list above? Your previous examples have listed different behavior between different servers on the same iso level name, but you have not given a different iso level that is on the list. |
@kardianos Your list for transaction isolation level is enough, except I will put IsoSnapshot before IsoSerializable. But you can have other vendor specific flags that can be passed to when starting a transaction, For example, Oracle has some flags to specify rollback segment for a transaction, and I don't think that is universal across vendor, you can see that here. And there other flags for the wire protocol here. Oh, and ReadOnly should be a access mode, not a isolation level. Another concern is that if you define those contants for user code in |
@noblehng Thank you for the pointers to the docs. While not exactly the same, another, separate issue is to support I'm open to having an API like:
That way the driver has the option to define additional flags, and the flags could contain data for the something like the Oracle
Used like:
I would be interested to hear what you and @bradfitz think of that. |
@kardianos Your example is basically what I was suggesting. |
@kardianos I think using a interface is overkill. For vendor specific settings, you will need to import the driver expicitly (not just register with a '_') anyway. So for more complex transaction settings, driver could provide helper functions like: And I still want to ask the reason to put Also the encoding of the Isolation constants could be more efficiently, as different isolation levels can't be used together. |
@noblehng I heard you express the user requirement "as a user, I want to enable usage such as Oracle's USE ROLLBACK SEGMENT (name)". I then see you are pushing the technical solution to have transactions be a uint32 flag set. Because the use rollback needs a (name), I don't see how your proposed technical solution satisfies your previous user requirement. |
@kardianos I said that above, it can be done with a For predefined constants, it could be like: const (
DefaultTx iota
// isolation level, 3bits
// Warning: The actual meanings of these levels are drive defined, use with cautious
ReadUncommitedTx
ReadCommitedTx
WriteCommitedTx
RepeatableReadTx
SnapshotTx
SerializableTx
LinearizableTx
// access mode, 2bits
ReadOnlyTx 0x1000
WriteOnlyTx 0x10000
ReadWriteTx 0x11000
// remaining bits are for custom driver defined flags
) Edit: |
CL https://golang.org/cl/29381 mentions this issue. |
If you don't mind me asking: when I first read the context package docs, I assumed I would do something like this to have per-request database transactions associated with per-request contexts:
Am I wrong about this, given all these new methods? I admittedly don't quite get what a context should store, apart from the general "request-scoped values". |
This doesn't change anything about where you keep your database or your transaction variables (which should not be in the context). Where this adds value is to allow for cancelation of queries and transactions for whatever purpose, plus adding some ability for extra tooling like traces, logs, etc. |
context does store values and that can be useful at times. But context is mostly about limiting the life span of requests in a consistent manner. Cancellation and timeout. This might be worthy of a blog post, but here is how you might use context here:
|
Ah, that makes more sense. Good to know; thanks! |
@kardianos wrote a doc summarizing what's new for https://docs.google.com/document/d/1F778e7ZSNiSmbju3jsEWzShcb8lIO4kDyfKDNm4PNd8/edit |
This bug is about adding Context support the
database/sql
package.Parent bug is #14660
The text was updated successfully, but these errors were encountered: