-
Notifications
You must be signed in to change notification settings - Fork 214
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
Reset db connection before returning it to the pool #6407
Conversation
We have seen in logs: > http: panic serving 127.0.0.1:34652: connection returned to pool has active statement: "select address, balance, next_nonce, max(layer_updated), template, state from accounts group by address order by layer_updated desc limit 100" as part of http handler recovery function is returning connection to the pool, while it can have pending, non finished statement. Code in this patch should fix this issue.
sql/database.go
Outdated
@@ -605,6 +605,15 @@ func (db *sqliteDatabase) getConn(ctx context.Context) *sqlite.Conn { | |||
return conn | |||
} | |||
|
|||
func (db *sqliteDatabase) returnConn(conn *sqlite.Conn) { |
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.
Maybe putConn
given that we have getConn
already and we're dealing with a pool with its "get"/"put" operations?
Also returnConn
sounds a bit misleading as it may sound as if it's dealing with return value of some kind
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.
In IT we have 2 main issues - naming and cache invalidation ;)
I was thinking too long on how to name this func :D but i'll take your suggestion.
if conn.CheckReset() != "" { | ||
ch := make(chan struct{}) | ||
conn.SetInterrupt(ch) | ||
close(ch) |
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 probably needs some comments explaining why it needs to happen this way
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.
Comment with explanation added.
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 you add some tests that fail without your change but pass with the adapted code?
Also: this feels like a hack that is necessary, because we might not be using contexts correctly in For instance: I feel like func (db *sqliteDatabase) withTx(ctx context.Context, initstmt string, exec func(Transaction) error) (err error) {
txCtx, cancel := context.WithCancel()
defer cancel()
tx, err := db.getTx(txCtx, initstmt)
if err != nil {
return err
}
defer func() {
if rErr := tx.Release(); rErr != nil && err == nil {
err = fmt.Errorf("release tx: %w", rErr)
}
}()
if err := exec(tx); err != nil {
tx.queryCache.ClearCache()
return err
}
return tx.Commit()
} If now the |
I just thought of a better approach than my previous suggestion: func (db *sqliteDatabase) getTx(ctx context.Context, initstmt string) (*sqliteTx, error) {
if db.closed {
return nil, ErrClosed
}
conCtx, cancel := context.WithCancel(ctx)
conn := db.getConn(conCtx)
if conn == nil {
cancel()
return nil, ErrNoConnection
}
tx := &sqliteTx{queryCache: db.queryCache, db: db, conn: conn, freeConn: cancel}
if err := tx.begin(initstmt); err != nil {
cancel()
// TODO: shouldn't the TX also be put back into the pool here?
return nil, err
}
return tx, nil
}
// sqliteTx is wrapper for database transaction.
type sqliteTx struct {
*queryCache
db *sqliteDatabase
conn *sqlite.Conn
freeConn func()
committed bool
err error
}
// Release transaction. Every transaction that was created must be released.
func (tx *sqliteTx) Release() error {
tx.freeConn()
defer tx.db.pool.Put(tx.conn)
if tx.committed {
return nil
}
stmt := tx.conn.Prep("ROLLBACK")
_, tx.err = stmt.Step()
return mapSqliteError(tx.err)
} |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #6407 +/- ##
=========================================
- Coverage 79.8% 79.7% -0.1%
=========================================
Files 328 328
Lines 42879 42893 +14
=========================================
- Hits 34221 34201 -20
- Misses 6725 6758 +33
- Partials 1933 1934 +1 ☔ View full report in Codecov by Sentry. |
IMO your proposed code will fail as cancel func is nowhere passed so it can not be called when needed - before returning connection to the pool. |
Closing in favor of more clean approach proposed in #6408 |
Motivation
We have seen in logs:
Code in this patch should fix this issue.
Test Plan
No new unittests added as the original issue was not reproduced.
TODO