2
0

stmtcache: Use deterministic, stable statement names

Statement names are now a function of the SQL. This may make database
diagnostics, monitoring, and profiling easier.
This commit is contained in:
Jack Christensen
2023-09-23 09:55:05 -05:00
parent bbe2653bc5
commit 7de53a958b
3 changed files with 21 additions and 12 deletions
+3 -3
View File
@@ -486,7 +486,7 @@ optionLoop:
} }
sd := c.statementCache.Get(sql) sd := c.statementCache.Get(sql)
if sd == nil { if sd == nil {
sd, err = c.Prepare(ctx, stmtcache.NextStatementName(), sql) sd, err = c.Prepare(ctx, stmtcache.StatementName(sql), sql)
if err != nil { if err != nil {
return pgconn.CommandTag{}, err return pgconn.CommandTag{}, err
} }
@@ -840,7 +840,7 @@ func (c *Conn) getStatementDescription(
} }
sd = c.statementCache.Get(sql) sd = c.statementCache.Get(sql)
if sd == nil { if sd == nil {
sd, err = c.Prepare(ctx, stmtcache.NextStatementName(), sql) sd, err = c.Prepare(ctx, stmtcache.StatementName(sql), sql)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -1019,7 +1019,7 @@ func (c *Conn) sendBatchQueryExecModeCacheStatement(ctx context.Context, b *Batc
bi.sd = distinctNewQueries[idx] bi.sd = distinctNewQueries[idx]
} else { } else {
sd = &pgconn.StatementDescription{ sd = &pgconn.StatementDescription{
Name: stmtcache.NextStatementName(), Name: stmtcache.StatementName(bi.query),
SQL: bi.query, SQL: bi.query,
} }
distinctNewQueriesIdxMap[sd.SQL] = len(distinctNewQueries) distinctNewQueriesIdxMap[sd.SQL] = len(distinctNewQueries)
+11 -1
View File
@@ -34,7 +34,8 @@ func (c *LRUCache) Get(key string) *pgconn.StatementDescription {
} }
// Put stores sd in the cache. Put panics if sd.SQL is "". Put does nothing if sd.SQL already exists in the cache. // Put stores sd in the cache. Put panics if sd.SQL is "". Put does nothing if sd.SQL already exists in the cache or
// sd.SQL has been invalidated and HandleInvalidated has not been called yet.
func (c *LRUCache) Put(sd *pgconn.StatementDescription) { func (c *LRUCache) Put(sd *pgconn.StatementDescription) {
if sd.SQL == "" { if sd.SQL == "" {
panic("cannot store statement description with empty SQL") panic("cannot store statement description with empty SQL")
@@ -44,6 +45,13 @@ func (c *LRUCache) Put(sd *pgconn.StatementDescription) {
return return
} }
// The statement may have been invalidated but not yet handled. Do not readd it to the cache.
for _, invalidSD := range c.invalidStmts {
if invalidSD.SQL == sd.SQL {
return
}
}
if c.l.Len() == c.cap { if c.l.Len() == c.cap {
c.invalidateOldest() c.invalidateOldest()
} }
@@ -73,6 +81,8 @@ func (c *LRUCache) InvalidateAll() {
c.l = list.New() c.l = list.New()
} }
// HandleInvalidated returns a slice of all statement descriptions invalidated since the last call to HandleInvalidated.
// Typically, the caller will then deallocate them.
func (c *LRUCache) HandleInvalidated() []*pgconn.StatementDescription { func (c *LRUCache) HandleInvalidated() []*pgconn.StatementDescription {
invalidStmts := c.invalidStmts invalidStmts := c.invalidStmts
c.invalidStmts = nil c.invalidStmts = nil
+7 -8
View File
@@ -2,18 +2,17 @@
package stmtcache package stmtcache
import ( import (
"strconv" "crypto/sha256"
"sync/atomic" "encoding/hex"
"github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgx/v5/pgconn"
) )
var stmtCounter int64 // StatementName returns a statement name that will be stable for sql across multiple connections and program
// executions.
// NextStatementName returns a statement name that will be unique for the lifetime of the program. func StatementName(sql string) string {
func NextStatementName() string { digest := sha256.Sum256([]byte(sql))
n := atomic.AddInt64(&stmtCounter, 1) return "stmtcache_" + hex.EncodeToString(digest[0:24])
return "stmtcache_" + strconv.FormatInt(n, 10)
} }
// Cache caches statement descriptions. // Cache caches statement descriptions.