Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions pkg/scdb/executor/ddl.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ func (e *DDLExec) executeCreateDatabase(s *ast.CreateDatabaseStmt) (err error) {
if result.Error != nil {
return fmt.Errorf("ddl.executeCreateDatabase: %v", result.Error)
}
// Invalidate cache after successful DDL operation
storage.InvalidateInfoSchemaCache(dbName)
return nil
}

Expand Down Expand Up @@ -212,6 +214,8 @@ func (e *DDLExec) executeCreateTable(s *ast.CreateTableStmt) (err error) {
}
}

// Invalidate cache after successful DDL operation
storage.InvalidateInfoSchemaCache(dbName)
return nil
}

Expand Down Expand Up @@ -321,6 +325,8 @@ func (e *DDLExec) executeCreateView(s *ast.CreateViewStmt) (err error) {
return fmt.Errorf("ddl.executeCreateView: %v", result.Error)
}
}
// Invalidate cache after successful DDL operation
storage.InvalidateInfoSchemaCache(dbName)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invalid的操作直接在调用 executeCreateView 的层级上统一做应该会更简洁。

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

return nil
}

Expand All @@ -346,6 +352,10 @@ func (e *DDLExec) executeDropDatabase(s *ast.DropDatabaseStmt) (err error) {
return fmt.Errorf("database %v not exists", dbName)
}
err = storage.NewDDLHandler(tx).DropSchema(model.NewCIStr(dbName))
if err == nil {
// Invalidate cache after successful DDL operation (in case not done in DropSchema)
storage.InvalidateInfoSchemaCache(dbName)
}
return err
}

Expand Down Expand Up @@ -404,5 +414,9 @@ func (e *DDLExec) executeDropTableOrView(s *ast.DropTableStmt) (err error) {
return err
}
err = storage.NewDDLHandler(tx).DropTable(model.NewCIStr(dbName), model.NewCIStr(tblName))
if err == nil {
// Invalidate cache after successful DDL operation (in case not done in DropTable)
storage.InvalidateInfoSchemaCache(dbName)
}
return err
}
4 changes: 4 additions & 0 deletions pkg/scdb/storage/schema_tables.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ func (h *DDLHandler) DropSchema(schema model.CIStr) error {
if err := h.store.Transaction(callFc); err != nil {
return fmt.Errorf("dropSchema: %v", err)
}
// Invalidate cache after successful DDL operation
InvalidateInfoSchemaCache(schema.String())
return nil
}

Expand All @@ -102,6 +104,8 @@ func (h *DDLHandler) DropTable(schema, tblName model.CIStr) error {
if err := h.store.Transaction(callFc); err != nil {
return fmt.Errorf("dropTable: %v", err)
}
// Invalidate cache after successful DDL operation
InvalidateInfoSchemaCache(schema.String())
return nil
}

Expand Down
55 changes: 55 additions & 0 deletions pkg/scdb/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"reflect"
"sort"
"strings"
"sync"

"github.com/sethvargo/go-password/password"
"gorm.io/gorm"
Expand Down Expand Up @@ -53,6 +54,14 @@
// keep creating order
var allTables = []interface{}{&User{}, &Database{}, &Table{}, &Column{}, &DatabasePriv{}, &TablePriv{}, &ColumnPriv{}}

// InfoSchema cache to avoid frequent database queries
var (
infoSchemaCache sync.Map // map[string]infoschema.InfoSchema, key is database name

Check failure on line 59 in pkg/scdb/storage/storage.go

View workflow job for this annotation

GitHub Actions / golangci-lint (/home/runner/work/scql/scql)

File is not properly formatted (gofmt)
infoCacheHitCount int64
infoCacheMissCount int64
infoCacheMutex sync.RWMutex
)

// NeedBootstrap checks if the store is empty
func NeedBootstrap(store *gorm.DB) bool {
for _, tn := range allTables {
Expand Down Expand Up @@ -173,6 +182,33 @@
return &user, nil
}

// InvalidateInfoSchemaCache invalidates the InfoSchema cache for a specific database
// If dbName is empty, it invalidates all cached InfoSchemas
func InvalidateInfoSchemaCache(dbName string) {
if dbName == "" {
// Clear all cache
infoSchemaCache = sync.Map{}
} else {
// Clear specific database cache
infoSchemaCache.Delete(dbName)
}
}

// ResetInfoSchemaCacheStats resets cache statistics (for testing)
func ResetInfoSchemaCacheStats() {
infoCacheMutex.Lock()
defer infoCacheMutex.Unlock()
infoCacheHitCount = 0
infoCacheMissCount = 0
}

// GetInfoSchemaCacheStats returns cache hit and miss statistics
func GetInfoSchemaCacheStats() (hits, misses int64) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这两个func只是为了测试用吗?可以看看是否适合加到metrics里面

infoCacheMutex.RLock()
defer infoCacheMutex.RUnlock()
return infoCacheHitCount, infoCacheMissCount
}

func QueryInfoSchema(store *gorm.DB) (result infoschema.InfoSchema, err error) {
callFc := func(tx *gorm.DB) error {
result, err = queryInfoSchema(tx)
Expand Down Expand Up @@ -244,13 +280,32 @@
}

func QueryDBInfoSchema(store *gorm.DB, dbName string) (result infoschema.InfoSchema, err error) {
// Try to get from cache first
if cached, ok := infoSchemaCache.Load(dbName); ok {
infoCacheMutex.Lock()
infoCacheHitCount++
infoCacheMutex.Unlock()
return cached.(infoschema.InfoSchema), nil
}

// Cache miss, query from database
infoCacheMutex.Lock()
infoCacheMissCount++
infoCacheMutex.Unlock()

callFc := func(tx *gorm.DB) error {
result, err = queryDBInfoSchema(tx, dbName)
return err
}
if err := store.Transaction(callFc, &sql.TxOptions{ReadOnly: true}); err != nil {
return nil, fmt.Errorf("queryDBInfoSchema: %v", err)
}

// Store in cache
if result != nil {
infoSchemaCache.Store(dbName, result)
}

return result, nil
}

Expand Down
Loading