Skip to content

Commit

Permalink
stmtcache: Use deterministic, stable statement names
Browse files Browse the repository at this point in the history
Statement names are now a function of the SQL. This may make database
diagnostics, monitoring, and profiling easier.
  • Loading branch information
jackc committed Sep 23, 2023
1 parent bbe2653 commit 7de53a9
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 12 deletions.
6 changes: 3 additions & 3 deletions conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@ optionLoop:
}
sd := c.statementCache.Get(sql)
if sd == nil {
sd, err = c.Prepare(ctx, stmtcache.NextStatementName(), sql)
sd, err = c.Prepare(ctx, stmtcache.StatementName(sql), sql)
if err != nil {
return pgconn.CommandTag{}, err
}
Expand Down Expand Up @@ -840,7 +840,7 @@ func (c *Conn) getStatementDescription(
}
sd = c.statementCache.Get(sql)
if sd == nil {
sd, err = c.Prepare(ctx, stmtcache.NextStatementName(), sql)
sd, err = c.Prepare(ctx, stmtcache.StatementName(sql), sql)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1019,7 +1019,7 @@ func (c *Conn) sendBatchQueryExecModeCacheStatement(ctx context.Context, b *Batc
bi.sd = distinctNewQueries[idx]
} else {
sd = &pgconn.StatementDescription{
Name: stmtcache.NextStatementName(),
Name: stmtcache.StatementName(bi.query),
SQL: bi.query,
}
distinctNewQueriesIdxMap[sd.SQL] = len(distinctNewQueries)
Expand Down
12 changes: 11 additions & 1 deletion internal/stmtcache/lru_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
if sd.SQL == "" {
panic("cannot store statement description with empty SQL")
Expand All @@ -44,6 +45,13 @@ func (c *LRUCache) Put(sd *pgconn.StatementDescription) {
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 {
c.invalidateOldest()
}
Expand Down Expand Up @@ -73,6 +81,8 @@ func (c *LRUCache) InvalidateAll() {
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 {
invalidStmts := c.invalidStmts
c.invalidStmts = nil
Expand Down
15 changes: 7 additions & 8 deletions internal/stmtcache/stmtcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@
package stmtcache

import (
"strconv"
"sync/atomic"
"crypto/sha256"
"encoding/hex"

"github.com/jackc/pgx/v5/pgconn"
)

var stmtCounter int64

// NextStatementName returns a statement name that will be unique for the lifetime of the program.
func NextStatementName() string {
n := atomic.AddInt64(&stmtCounter, 1)
return "stmtcache_" + strconv.FormatInt(n, 10)
// StatementName returns a statement name that will be stable for sql across multiple connections and program
// executions.
func StatementName(sql string) string {
digest := sha256.Sum256([]byte(sql))
return "stmtcache_" + hex.EncodeToString(digest[0:24])
}

// Cache caches statement descriptions.
Expand Down

0 comments on commit 7de53a9

Please sign in to comment.