-
Notifications
You must be signed in to change notification settings - Fork 5
perf(dotc1z): add prepared statement cache for repeated SQL queries #864
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -67,6 +67,11 @@ type C1File struct { | |||||||||||||||||||||||||||||||||||||||
| slowQueryThreshold time.Duration | ||||||||||||||||||||||||||||||||||||||||
| slowQueryLogFrequency time.Duration | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| // Prepared statement cache: keyed by SQL text, lazily populated. | ||||||||||||||||||||||||||||||||||||||||
| // With MaxOpenConns(1) all stmts are bound to the single connection. | ||||||||||||||||||||||||||||||||||||||||
| stmtCache map[string]*sql.Stmt | ||||||||||||||||||||||||||||||||||||||||
| stmtCacheMu sync.Mutex | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| // Sync cleanup settings | ||||||||||||||||||||||||||||||||||||||||
| syncLimit int | ||||||||||||||||||||||||||||||||||||||||
| skipCleanup bool | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -184,6 +189,7 @@ func NewC1File(ctx context.Context, dbFilePath string, opts ...C1FOption) (*C1Fi | |||||||||||||||||||||||||||||||||||||||
| slowQueryThreshold: 5 * time.Second, | ||||||||||||||||||||||||||||||||||||||||
| slowQueryLogFrequency: 1 * time.Minute, | ||||||||||||||||||||||||||||||||||||||||
| encoderConcurrency: 1, | ||||||||||||||||||||||||||||||||||||||||
| stmtCache: make(map[string]*sql.Stmt), | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| for _, opt := range opts { | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -492,12 +498,53 @@ func (c *C1File) closeRawDB(ctx context.Context) error { | |||||||||||||||||||||||||||||||||||||||
| _, span := tracer.Start(ctx, "C1File.closeRawDB") | ||||||||||||||||||||||||||||||||||||||||
| var err error | ||||||||||||||||||||||||||||||||||||||||
| defer func() { uotel.EndSpanWithError(span, err) }() | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| c.stmtCacheMu.Lock() | ||||||||||||||||||||||||||||||||||||||||
| for _, stmt := range c.stmtCache { | ||||||||||||||||||||||||||||||||||||||||
| stmt.Close() | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| c.stmtCache = nil | ||||||||||||||||||||||||||||||||||||||||
| c.stmtCacheMu.Unlock() | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| err = c.rawDb.Close() | ||||||||||||||||||||||||||||||||||||||||
| c.rawDb = nil | ||||||||||||||||||||||||||||||||||||||||
| c.db = nil | ||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| func (c *C1File) getOrPrepare(ctx context.Context, query string) (*sql.Stmt, error) { | ||||||||||||||||||||||||||||||||||||||||
| c.stmtCacheMu.Lock() | ||||||||||||||||||||||||||||||||||||||||
| if c.stmtCache == nil { | ||||||||||||||||||||||||||||||||||||||||
| c.stmtCacheMu.Unlock() | ||||||||||||||||||||||||||||||||||||||||
| return nil, ErrDbNotOpen | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| if stmt, ok := c.stmtCache[query]; ok { | ||||||||||||||||||||||||||||||||||||||||
| c.stmtCacheMu.Unlock() | ||||||||||||||||||||||||||||||||||||||||
| return stmt, nil | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| c.stmtCacheMu.Unlock() | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| stmt, err := c.rawDb.PrepareContext(ctx, query) | ||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||
| return nil, fmt.Errorf("getOrPrepare: %w", err) | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| c.stmtCacheMu.Lock() | ||||||||||||||||||||||||||||||||||||||||
| if c.stmtCache == nil { | ||||||||||||||||||||||||||||||||||||||||
| c.stmtCacheMu.Unlock() | ||||||||||||||||||||||||||||||||||||||||
| stmt.Close() | ||||||||||||||||||||||||||||||||||||||||
| return nil, ErrDbNotOpen | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| if existing, ok := c.stmtCache[query]; ok { | ||||||||||||||||||||||||||||||||||||||||
| c.stmtCacheMu.Unlock() | ||||||||||||||||||||||||||||||||||||||||
| stmt.Close() | ||||||||||||||||||||||||||||||||||||||||
| return existing, nil | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| c.stmtCache[query] = stmt | ||||||||||||||||||||||||||||||||||||||||
| c.stmtCacheMu.Unlock() | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+539
to
+544
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Suggestion: The new nil guard at the top of
Suggested change
|
||||||||||||||||||||||||||||||||||||||||
| return stmt, nil | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| // truncateWAL truncates the WAL file. | ||||||||||||||||||||||||||||||||||||||||
| // Returns the busy, log, and checkpointed values. | ||||||||||||||||||||||||||||||||||||||||
| func (c *C1File) truncateWAL(ctx context.Context) (int, int, int, error) { | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.
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.
[🤖] P1:
getOrPreparecan still panic on the cache-miss path ifcloseRawDBruns between preparing the new statement and re-acquiringstmtCacheMu.closeRawDBsetsc.stmtCache = nil, so this write can become a write to a nil map. Please mirror the entry nil check after the second lock, close the newly prepared statement, and returnErrDbNotOpenbefore checking/storing the cache entry.