-
Notifications
You must be signed in to change notification settings - Fork 116
go sql: add option to panic on non-indexed queries #394
Conversation
Schema: schema, | ||
Conn: conn, | ||
Schema: schema, | ||
panicOnNoIndex: false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could let the false
be implicit, but I sort of prefer being explicit on this type of stuff.
Pull Request Test Coverage Report for Build 2944
💛 - Coveralls |
0f763f4
to
1a2ffe7
Compare
b8676bc
to
1d9d8ee
Compare
db, err = db.WithPanicOnNoIndex() | ||
|
||
// A second set will error | ||
_, err = db.WithPanicOnNoIndex() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ooc why do we want to panic if we set this a second time? why not just fail silently since there is no change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It errors, not panics, and I tend to agree with you on silently running, but other functions similar to this in the file error out. It does make sense to some extent since it indicates an odd db setup from the caller.
assert.NoError(t, err) | ||
defer tdb.Close() | ||
|
||
db, err = db.WithPanicOnNoIndex() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a safe change to merge into production. Can we safely know we haven't missed a query that doesn't use the index?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not currently on in production for us. There will be followup PRs where we:
- only turn this on in test envs (so yes it's safe)
- in that PR, we will manually tag all full scan queries as such (we know them all since they'll panic and fail tests, and if there are untested codepaths, this won't be on in production)
@@ -134,6 +138,15 @@ func (db *DB) WithShardLimit(shardLimit Filter) (*DB, error) { | |||
return &dbCopy, nil | |||
} | |||
|
|||
func (db *DB) WithPanicOnNoIndex() (*DB, error) { | |||
if db.panicOnNoIndex { | |||
return nil, errors.New("already is set panic on no index") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why returning an error? Would a noop enough?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We only want to use this in test right? Can we add a comment to call it out explicitly? I am worried that people use it in production and then cause outages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I previously had this called IsTest
but thought to name it according to what it does. I'd happily add a comment as to what behavior this controls though!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above comment from emma re noop. I'm fine with a noop, but other similar functions in this file return an error, so I matched behavior.
sqlgen/db.go
Outdated
} | ||
|
||
explainRes, err := parseExplainResults(res) | ||
res.Close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prefer defer res.Close() right after the call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't use defer res.Close() here (I had it that way initially). We need to close this query before executing the main query after the explain check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can do this now that it's a helper!! woot
@@ -252,6 +292,36 @@ func (db *DB) BaseQuery(ctx context.Context, query *BaseSelectQuery) ([]interfac | |||
|
|||
clause, args := selectQuery.ToSQL() | |||
|
|||
if db.panicOnNoIndex && (query.Options == nil || !query.Options.AllowNoIndex) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make this branch a helper function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure!
1d9d8ee
to
491db21
Compare
sqlgen/db.go
Outdated
explainRes, err := parseExplainResults(res) | ||
|
||
// If our explain parsing fails, we don't analyze the response or fail the query. | ||
if err == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return if err != nil, so we can save an intent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do you mean "save an intent"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry, i mean save a tab...
Also, we should return err is err != nil
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discussed offline, we're going to fail the query if explain parsing fails.
491db21
to
ed5411f
Compare
In this commit we add the ability to instrument a connection with a boolean that, when enabled, will have a query panic when no index is present in a sql explain for that query. We also add the ability to bypass this check on any given query in SelectOptions. Additionally we add some thin wrappers to our query functions to set this option. This will permit us to enable this check in tests and have people explicitly make full table scan queries when they fully intend to. Implementation detail: We check both key and possible keys since there are times where key will be non-null (eg PRIMARY) while possible keys is null, and also times when key is null, but possible keys is not null (eg when mysql chooses a full scan despite the presence of an index due to small table size)
ed5411f
to
6a7cdb8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sgtm!!
In this commit we add the ability to instrument a connection
with a boolean that, when enabled, will have a query panic
when no index is present in a sql explain for that query.
We also add the ability to bypass this check on any given query
in SelectOptions. Additionally we add some thin wrappers to our
query functions to set this option.
This will permit us to enable this check in
tests and have people explicitly make full table scan queries
when they fully intend to.
Implementation detail:
We check both key and possible keys since there are times where key
will be non-null (eg PRIMARY) while possible keys is null, and also
times when key is null, but possible keys is not null (eg when
mysql chooses a full scan despite the presence of an index due to small
table size)