-
Notifications
You must be signed in to change notification settings - Fork 379
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
Use a separate unit to manage the cache of prepared statements #2937
base: master
Are you sure you want to change the base?
Conversation
/gcbrun |
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.
How good is the test coverage for this? Not sure if it's worth having dedicated tests but worth considering perhaps.
storage/sqlutil/stmt_cache.go
Outdated
@@ -0,0 +1,246 @@ | |||
package sqlutil |
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.
The package name is generic. I'd suggest naming it after the main functionality it contains (the statement cache).
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.
The package name is generic. I'd suggest naming it after the main functionality it contains (the statement cache).
fixed
@@ -7,6 +7,8 @@ | |||
different version can lead to presubmits failing due to unexpected | |||
diffs. | |||
|
|||
* Use a separate unit (StmtCache) to manage the cache of prepared statements, which wraps the sql.Stmt struct to handle and monitor the execution errors of the prepared statement. When an error occurs during statement execution, it closes the statement and clears the cache, as well as increments the error monitoring indicator. |
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.
Not sure what 'unit' means?
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.
The word 'separate unit' comes from the current TODO comment:
trillian/storage/mysql/tree_storage.go
Line 109 in bc857c2
// TODO(al,martin): consider pulling this all out as a separate unit for reuse |
storage/sqlutil/stmt_cache.go
Outdated
} | ||
|
||
// errHandler handling and monitoring SQL errors | ||
// The err parameter standby |
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.
Not sure what 'standby' means?
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.
Comments have been updated.
storage/sqlutil/stmt_cache.go
Outdated
// var name string | ||
// err := nameByUseridStmt.QueryRow(id).Scan(&name) | ||
// | ||
// QueryRow uses context.Background internally; to specify the context, use |
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.
Are there cases where the background context is still used? Might be worth deprecating this?
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.
removed
storage/sqlutil/stmt_cache.go
Outdated
} | ||
|
||
// NewStmtCache creates a StmtCache instance. | ||
func NewStmtCache(db *sql.DB, mf monitoring.MetricFactory) *StmtCache { |
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 think this can just be 'New'.
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.
fixed
storage/sqlutil/stmt_cache.go
Outdated
return strings.Replace(sql, PlaceholderSQL, parameters, 1) | ||
} | ||
|
||
// GetStmt creates and caches sql.Stmt structs and returns their wrapper structs Stmt. |
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 think 'structs' can be removed as this should be clear from context.
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.
fixed
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.
The behaviour of this PR is slightly different to #2936 in that I think here we'll only remove the statement which errored from the cache (the others will remain until they too error out) - is that intentional?
The other thing which I think is worth discussing is the relative size of the two PRs - this one feels like it's going for a "cleaner" way of approaching it, but the trade-off is that it's 10x the LoC.
#2936 is very small in comparison, and very explicit, but could miss a call site (although since the entire cache is flushed on any error that would likely mitigate the effect missed call sites).
Any thoughts, @px3303 @Martin2112 @mhutchinson @google/trillian-team?
storage/stmtcache/stmtcache_test.go
Outdated
// _, err := cache.GetStmt(ctx, sql, 1, "?", "") | ||
// if err != nil { | ||
// t.Fatalf("failed to cache.GetStmt: %s", err) | ||
// } |
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.
Would calling db.Close()
after creating the stmt but before using it let you trigger the cache clear logic?
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, that part was submitted before it was completed. It has now been completed.
storage/stmtcache/stmtcache.go
Outdated
sc.statementMutex.Lock() | ||
defer sc.statementMutex.Unlock() | ||
|
||
if sc.statements[s.statement] != nil && sc.statements[s.statement][s.placeholderNum] == s.stmt { |
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 think you might be able to write this as:
if s, ok := sc.statements[s.statement][s.placeholderNum]; ok && s == s.stmt { ... }
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 think you might be able to write this as:
if s, ok := sc.statements[s.statement][s.placeholderNum]; ok && s == s.stmt { ... }
Thanks, fixed.
storage/stmtcache/stmtcache.go
Outdated
errStmtCounter monitoring.Counter | ||
) | ||
|
||
// PlaceholderSQL SQL statement placeholder |
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.
nit: missing trailing full-stops on a few of the comments.
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.
fixed
I have added some test cases, but some situations cannot be simulated. For example, the prepared statement disappeared in the database for unknown reasons. Currently, we have encountered this problem in the production environment. A failure in the AWS RDS database caused the prepared statement to fail, and an error was reported: Unknown prepared statement handler (17) gave to mysql_ stmt_ precheck。 When this happens, the program does not automatically resume. |
Because in high concurrency situations, clearing all caches may cause a loop problem.
This PR wants to handle it more cleanly, and also wants to monitor SQL errors. Currently, these SQL errors only print logs, and we cannot detect them in a timely manner. |
I wonder if we should be looking at the other option of removing the statement cache? It's not clear to me what the performance impact would be and whether it would be acceptable. The Golang driver does not have the same option as Java for retaining prepared statements: https://docs.oracle.com/cd/B19306_01/java.102/b14355/stmtcach.htm#i1071993. |
This PR is an improved version of #2936, with the following changes:
1.Use a separate unit (StmtCache) to manage the cache of prepared statements.
2.Wrap sql.Stmt struct to handle monitoring prepared statement execution errors.
3.When a statement execution error occurs, only clear the corresponding prepared statement cache, preventing circular cache clearing problems.
4.Add prepared statement execution error metrics.
Checklist