-
Notifications
You must be signed in to change notification settings - Fork 17.9k
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: support tracing queries #18080
Comments
I would be happy to help with this. |
I don't know if this is the best way to go, but here is the direction I've been thinking:
I would think that this From a driver maintainer's perspective, I think it would be sweet to have a context value I could ask a user encountering a bug to instrument their query with, then on error, grab the trace and return it with the error. Then when I get the error report I can see all the way through to the driver protocol and messages. From a line of business application maintainer's perspective I'd like a comprehensive way to instrument my queries to get timing information out of them, bubbling up any queries that take a long time exposing the query text and parameters used for it. @kevpie What do you think of this? Do you think you could propose something concrete based on this or an alternate design? |
@rsc Before I go down this road too far, one thing on your recent blog post is you'd like to clarify what does and doesn't go into context values. I think something like this makes sense. I know others would push back on having something like a logger in a context. Any guidance on what would be acceptable would be great. |
@kardianos I was thinking it could follow in the spirit of https://golang.org/pkg/net/http/httptrace/ I am using the |
@kardianos Try it and see. I don't know for sure but it's something that seems reasonable. It doesn't have the problems that transaction isolation mode did. |
@luna-duclos do you have any opinion on this topic? https://github.com/ExpansiveWorlds/instrumentedsql is an interesting take on this. |
Unlike HTTP, I see very few things that an sql driver doesn't already have access to to trace, so I think a wrapping driver is mostly the correct approach. There are a few mechanisms however that are managed by the database/sql package and are completely invisible to the drivers, connection pooling being the primary one of those. |
@luna-duclos I generally like this approach. It nicely removes the tracing logic away from the rest of the SQL logic. Instrumenting the connection pool is also a good idea. Right now the connection pool is too opaque. If it isn't too invasive, I'll see what I can work up. I think it will be alright, the calls to get/put new conns are fairly centralized. However, this information would need to be attached at the driver level and not the request level I think. Could you propose an interface on your existing instrumentedsql package report conn pool stats? Off the top of my head, it seems we could define:
But I'm fairly sure that isn't right. |
Some time ago I proposed a (MySQL) driver-level implementation: go-sql-driver/mysql#445 |
I'm removing this milestone for now until more people have experience with @luna-duclos method and interface. |
FYI: I'm developing https://github.com/shogo82148/go-sql-proxy. |
I started playing with github.com/opentracing/opentracing-go and wrapping the database/sql. Most things are easy to be wrapped except for transactions. Ideally I'd like to create a drop-in replacement so that my new db objects satisfy the existing interfaces. The problem is that the transactions are structs and can't really be wrapped without using a custom type. |
Have a look at https://github.com/ExpansiveWorlds/instrumentedsql, it
provides exactly what you're looking for and can wrap any arbitrary SQL
driver to provide tracing.
…On Mon, Dec 4, 2017 at 1:25 PM, Pavel Nikolov ***@***.***> wrote:
I started playing with github.com/opentracing/opentracing-go and wrapping
the database/sql. Most things are easy to be wrapped except for
transactions. Ideally I'd like to create a drop-in replacement so that my
new db objects satisfy the existing interfaces. The problem is that the
transactions are structs and can't really be wrapped without using a custom
type.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#18080 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AFUXAMHkeaFtI0SWolr_FoNF9m8AYuhDks5s8-SzgaJpZM4K-KLA>
.
|
Anecdote: I spent some time this week trying to figure out what was slowing down my (Postgres) SQL queries, and it turned out that connection pooling was disabled. The tracing solutions mentioned here do not show this. (I would question the statement that it is opaque to drivers: I was able to show connections being created by inserting |
@luna-duclos would you recommend using your fork at https://github.com/luna-duclos/instrumentedsql now? |
I started a thread[1] on the golang nuts mailing list about something similar. tldr; wrapping a driver is clearly a viable approach, however I think there are some benefits to including this in the core sql package, for convenience I include my reasoning from the mailing list here: In my view, there are two benefits to adding it to the main sql package
[1] https://groups.google.com/forum/#!topic/golang-nuts/g8rBUu9MMmw |
@ConradWoodGuru I am interested in the interceptor concept. What would the interceptor interface look like? Would it look something like this:
|
sorry for the late answer - github is not on my "closely being monitored list" of apps. I'll try to check this "issue" regularly from now on. as per the answer:. I would try to simplify the interceptor (so that it is easy to implement only the parts necessary). type Interceptor interface { the functions are probably something like: func (m *myInterceptor) beginDatabaseRequest(...) error { func (m *myInterceptor) completeDatabaseRequest(...) error { I realise this is somewhat unusual but it has a couple of benefits:
Saying that - the interfaces you posted will solve my usecase too and are a great improvement, so if that's the way you want to go - I am happy to go with that! wdyt? |
Yes |
Hiya all. We needed something like this at ngrok with a bit more flexibility so I put this together: https://github.com/ngrok/sqlmw I think this is a pretty general purpose abstraction that allows for easy implementations of instrumentation like tracing in addition to support more powerful behaviors like caching, query rewriting, etc that require intermediating the calls. The API is more or less what @kardianos proposed and I ended up forking @luna-duclos's branch of instrumentedsql to create the code. I took to heart @ConradWoodGuru's suggestion that you should only need to override the parts of the intercepting interface that you want to use by creating the NullInterceptor concept that you embed into your own. Would appreciate feedback from everyone here who's had this problem. Lastly, a quick example of what it looks like to use the API: func run(dsn string) {
// install the wrapped driver
sql.Register("postgres-mw", sqlmw.Driver(pq.Dirver{}, new(sqlInterceptor)))
db, err := sql.Open("postgres-mw", dsn)
...
}
type sqlInterceptor struct {
sqlmw.NullInterceptor
}
func (in *sqlInterceptor) StmtQueryContext(ctx context.Context, conn driver.StmtQueryContext, query string, args []driver.NamedValue) (driver.Rows, error) {
startedAt := time.Now()
rows, err := conn.QueryContext(ctx, args)
log.Debug("executed sql query", "duration", time.Since(startedAt), "query", query, "args", args, "err", err)
return rows, err
} |
The current setup for wrapping drivers is fairly painful to work with if you want minimal impact on the original code. You can't just wrap a *sql.DB the user has passed to you. You either have to open it for them (doing wierd round trips to get the driver.Driver, and name, and register a new name, and re-open with the wrapped driver), or wrap the driver in advance, and have them open their DB using the wrapped driver name (which isn't too bad), but doing it that way means we don't have access to DBStats, which would be a nice easy way to get some simple metrics. |
@tcolgate |
Sqlhooks provides the most simple option, a query start , finish and onerror approach. It's too simple for rich tracing, but ... |
DBStats is good for connection pool metrics, but it would be nice to be able to trace through that process. Ideally you'd be able to see query submission to the API, through connection allocation, and the actual server hand off, with stuff around tx commit and rollback too. |
It's not sure what you're trying to achieve from the original description but if you're looking for full observability then I would suggest https://www.solarwinds.com/database-performance-monitor previously named vividcortex. |
We looked into creating a wrapping driver just to detect connection leaks and it didn't seem workable for many of the reasons mentioned above. We ended up forking and modifying the mysql driver instead. |
@jeffreydpayne What was missing with the Interceptor driver by @inconshreveable? Edit: I found some missing interception callbacks and I opened an issue. |
@inconshreveable Thank you for sqlmw! I found it to be incredibly useful. Here's a sample caching middleware that uses sqlmw - https://github.com/prashanthpai/sqlcache |
The database now supports passing context to query methods and to the drivers.
Look into supporting some sort of trace functionality for the sql package.
The text was updated successfully, but these errors were encountered: