From 29d50994aec0cfbde8dfbb926c3ac5821e9a33aa Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 24 Aug 2021 22:59:25 +0800 Subject: [PATCH] add function Close for DB for package gdb --- database/gdb/gdb.go | 25 ++++++++++++++++--------- database/gdb/gdb_core.go | 22 ++++++++++++++++++++++ database/gdb/gdb_driver_oracle.go | 28 ---------------------------- frame/gins/gins_database.go | 11 ++++------- 4 files changed, 42 insertions(+), 44 deletions(-) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 49dd32c3a..35e392caa 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -73,6 +73,14 @@ type DB interface { // Also see Core.Ctx. Ctx(ctx context.Context) DB + // Close closes the database and prevents new queries from starting. + // Close then waits for all queries that have started processing on the server + // to finish. + // + // It is rare to Close a DB, as the DB handle is meant to be + // long-lived and shared between many goroutines. + Close(ctx context.Context) error + // =========================================================================== // Query APIs. // =========================================================================== @@ -94,7 +102,7 @@ type DB interface { Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Delete. // =========================================================================== - // Internal APIs for CURD, which can be overwrote for custom CURD implements. + // Internal APIs for CURD, which can be overwritten by custom CURD implements. // =========================================================================== DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) // See Core.DoGetAll. @@ -179,6 +187,7 @@ type Core struct { group string // Configuration group name. debug *gtype.Bool // Enable debug mode for the database, which can be changed in runtime. cache *gcache.Cache // Cache manager, SQL result cache only. + links *gmap.StrAnyMap // links caches all created links by node. schema *gtype.String // Custom schema for this object. logger *glog.Logger // Logger for logging functionality. config *ConfigNode // Current config node. @@ -302,9 +311,6 @@ var ( // in the field name as it conflicts with "db.table.field" pattern in SOME situations. regularFieldNameWithoutDotRegPattern = `^[\w\-]+$` - // internalCache is the memory cache for internal usage. - internalCache = gcache.New() - // tableFieldsMap caches the table information retrived from database. tableFieldsMap = gmap.New(true) @@ -347,6 +353,7 @@ func New(group ...string) (db DB, err error) { group: groupName, debug: gtype.NewBool(), cache: gcache.New(), + links: gmap.NewStrAnyMap(true), schema: gtype.NewString(), logger: glog.New(), config: node, @@ -441,7 +448,7 @@ func getConfigNodeByWeight(cg ConfigGroup) *ConfigNode { for i := 0; i < len(cg); i++ { total += cg[i].Weight * 100 } - // If total is 0 means all of the nodes have no weight attribute configured. + // If total is 0 means all the nodes have no weight attribute configured. // It then defaults each node's weight attribute to 1. if total == 0 { for i := 0; i < len(cg); i++ { @@ -490,7 +497,7 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error node = &n } // Cache the underlying connection pool object by node. - v, _ := internalCache.GetOrSetFuncLock(node.String(), func() (interface{}, error) { + v := c.links.GetOrSetFuncLock(node.String(), func() interface{} { intlog.Printf( c.db.GetCtx(), `open new connection, master:%#v, config:%#v, node:%#v`, @@ -510,7 +517,7 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error sqlDb, err = c.db.Open(node) if err != nil { - return nil, err + return nil } if c.config.MaxIdleConnCount > 0 { @@ -534,8 +541,8 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error } else { sqlDb.SetConnMaxLifetime(defaultMaxConnLifeTime) } - return sqlDb, nil - }, 0) + return sqlDb + }) if v != nil && sqlDb == nil { sqlDb = v.(*sql.DB) } diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 34954d65b..5a63e285c 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -12,6 +12,7 @@ import ( "database/sql" "fmt" "github.com/gogf/gf/errors/gcode" + "github.com/gogf/gf/internal/intlog" "reflect" "strings" @@ -95,6 +96,27 @@ func (c *Core) GetCtxTimeout(timeoutType int, ctx context.Context) (context.Cont return ctx, func() {} } +// Close closes the database and prevents new queries from starting. +// Close then waits for all queries that have started processing on the server +// to finish. +// +// It is rare to Close a DB, as the DB handle is meant to be +// long-lived and shared between many goroutines. +func (c *Core) Close(ctx context.Context) (err error) { + c.links.LockFunc(func(m map[string]interface{}) { + for k, v := range m { + if db, ok := v.(*sql.DB); ok { + err = db.Close() + intlog.Printf(ctx, `close link: %s, err: %v`, k, err) + if err != nil { + return + } + } + } + }) + return +} + // Master creates and returns a connection from master node if master-slave configured. // It returns the default connection if master-slave not configured. func (c *Core) Master(schema ...string) (*sql.DB, error) { diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index d3e3d157e..cc308b687 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -236,34 +236,6 @@ FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '%s' ORDER BY COLUMN_ID`, return } -func (d *DriverOracle) getTableUniqueIndex(table string) (fields map[string]map[string]string, err error) { - table = strings.ToUpper(table) - v, _ := internalCache.GetOrSetFunc( - "table_unique_index_"+table, - func() (interface{}, error) { - res := (Result)(nil) - res, err = d.db.GetAll(fmt.Sprintf(` - SELECT INDEX_NAME,COLUMN_NAME,CHAR_LENGTH FROM USER_IND_COLUMNS - WHERE TABLE_NAME = '%s' - AND INDEX_NAME IN(SELECT INDEX_NAME FROM USER_INDEXES WHERE TABLE_NAME='%s' AND UNIQUENESS='UNIQUE') - ORDER BY INDEX_NAME,COLUMN_POSITION`, table, table)) - if err != nil { - return nil, err - } - fields := make(map[string]map[string]string) - for _, v := range res { - mm := make(map[string]string) - mm[v["COLUMN_NAME"].String()] = v["CHAR_LENGTH"].String() - fields[v["INDEX_NAME"].String()] = mm - } - return fields, nil - }, 0) - if err == nil { - fields = v.(map[string]map[string]string) - } - return -} - // DoInsert inserts or updates data for given table. // This function is usually used for custom interface definition, you do not need call it manually. // The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. diff --git a/frame/gins/gins_database.go b/frame/gins/gins_database.go index e27f7ce73..dfe83eae7 100644 --- a/frame/gins/gins_database.go +++ b/frame/gins/gins_database.go @@ -50,29 +50,26 @@ func Database(name ...string) gdb.DB { configMap = Config().GetMap(configNodeKey) } if len(configMap) == 0 && !gdb.IsConfigured() { - configFilePath, err := Config().GetFilePath() + configFilePath, _ := Config().GetFilePath() if configFilePath == "" { exampleFileName := "config.example.toml" if exampleConfigFilePath, _ := Config().GetFilePath(exampleFileName); exampleConfigFilePath != "" { - panic(gerror.WrapCodef( + panic(gerror.NewCodef( gcode.CodeMissingConfiguration, - err, `configuration file "%s" not found, but found "%s", did you miss renaming the example configuration file?`, Config().GetFileName(), exampleFileName, )) } else { - panic(gerror.WrapCodef( + panic(gerror.NewCodef( gcode.CodeMissingConfiguration, - err, `configuration file "%s" not found, did you miss the configuration file or the misspell the configuration file name?`, Config().GetFileName(), )) } } - panic(gerror.WrapCodef( + panic(gerror.NewCodef( gcode.CodeMissingConfiguration, - err, `database initialization failed: "%s" node not found, is configuration file or configuration node missing?`, configNodeNameDatabase, ))