make stmt cache size configurable
This commit is contained in:
		
							parent
							
								
									93eb7c6b83
								
							
						
					
					
						commit
						756df9385f
					
				| @ -18,4 +18,5 @@ const ( | |||||||
| 	MaxIdleConnsKey    = "MaxIdleConns" | 	MaxIdleConnsKey    = "MaxIdleConns" | ||||||
| 	MaxOpenConnsKey    = "MaxOpenConns" | 	MaxOpenConnsKey    = "MaxOpenConns" | ||||||
| 	ConnMaxLifetimeKey = "ConnMaxLifetime" | 	ConnMaxLifetimeKey = "ConnMaxLifetime" | ||||||
|  | 	MaxStmtCacheSize   = "MaxStmtCacheSize" | ||||||
| ) | ) | ||||||
|  | |||||||
| @ -109,8 +109,9 @@ func (ac *_dbCache) getDefault() (al *alias) { | |||||||
| 
 | 
 | ||||||
| type DB struct { | type DB struct { | ||||||
| 	*sync.RWMutex | 	*sync.RWMutex | ||||||
| 	DB             *sql.DB | 	DB                  *sql.DB | ||||||
| 	stmtDecorators *lru.Cache | 	stmtDecorators      *lru.Cache | ||||||
|  | 	stmtDecoratorsLimit int | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var _ dbQuerier = new(DB) | var _ dbQuerier = new(DB) | ||||||
| @ -165,16 +166,14 @@ func (d *DB) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (d *DB) Exec(query string, args ...interface{}) (sql.Result, error) { | func (d *DB) Exec(query string, args ...interface{}) (sql.Result, error) { | ||||||
| 	sd, err := d.getStmtDecorator(query) | 	return d.ExecContext(context.Background(), query, args...) | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	stmt := sd.getStmt() |  | ||||||
| 	defer sd.release() |  | ||||||
| 	return stmt.Exec(args...) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (d *DB) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { | func (d *DB) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { | ||||||
|  | 	if d.stmtDecorators == nil { | ||||||
|  | 		return d.DB.ExecContext(ctx, query, args...) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	sd, err := d.getStmtDecorator(query) | 	sd, err := d.getStmtDecorator(query) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @ -185,16 +184,14 @@ func (d *DB) ExecContext(ctx context.Context, query string, args ...interface{}) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (d *DB) Query(query string, args ...interface{}) (*sql.Rows, error) { | func (d *DB) Query(query string, args ...interface{}) (*sql.Rows, error) { | ||||||
| 	sd, err := d.getStmtDecorator(query) | 	return d.QueryContext(context.Background(), query, args...) | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	stmt := sd.getStmt() |  | ||||||
| 	defer sd.release() |  | ||||||
| 	return stmt.Query(args...) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (d *DB) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { | func (d *DB) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { | ||||||
|  | 	if d.stmtDecorators == nil { | ||||||
|  | 		return d.DB.QueryContext(ctx, query, args...) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	sd, err := d.getStmtDecorator(query) | 	sd, err := d.getStmtDecorator(query) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @ -205,24 +202,21 @@ func (d *DB) QueryContext(ctx context.Context, query string, args ...interface{} | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (d *DB) QueryRow(query string, args ...interface{}) *sql.Row { | func (d *DB) QueryRow(query string, args ...interface{}) *sql.Row { | ||||||
| 	sd, err := d.getStmtDecorator(query) | 	return d.QueryRowContext(context.Background(), query, args...) | ||||||
| 	if err != nil { |  | ||||||
| 		panic(err) |  | ||||||
| 	} |  | ||||||
| 	stmt := sd.getStmt() |  | ||||||
| 	defer sd.release() |  | ||||||
| 	return stmt.QueryRow(args...) |  | ||||||
| 
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (d *DB) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row { | func (d *DB) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row { | ||||||
|  | 	if d.stmtDecorators == nil { | ||||||
|  | 		return d.DB.QueryRowContext(ctx, query, args...) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	sd, err := d.getStmtDecorator(query) | 	sd, err := d.getStmtDecorator(query) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		panic(err) | 		panic(err) | ||||||
| 	} | 	} | ||||||
| 	stmt := sd.getStmt() | 	stmt := sd.getStmt() | ||||||
| 	defer sd.release() | 	defer sd.release() | ||||||
| 	return stmt.QueryRowContext(ctx, args) | 	return stmt.QueryRowContext(ctx, args...) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type TxDB struct { | type TxDB struct { | ||||||
| @ -345,14 +339,31 @@ func detectTZ(al *alias) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func addAliasWthDB(aliasName, driverName string, db *sql.DB) (*alias, error) { | func addAliasWthDB(aliasName, driverName string, db *sql.DB, params ...common.KV) (*alias, error) { | ||||||
|  | 	kvs := common.NewKVs(params...) | ||||||
|  | 
 | ||||||
|  | 	var stmtCache *lru.Cache | ||||||
|  | 	var stmtCacheSize int | ||||||
|  | 
 | ||||||
|  | 	maxStmtCacheSize := kvs.GetValueOr(MaxStmtCacheSize, 0).(int) | ||||||
|  | 	if maxStmtCacheSize > 0 { | ||||||
|  | 		_stmtCache, errC := newStmtDecoratorLruWithEvict(maxStmtCacheSize) | ||||||
|  | 		if errC != nil { | ||||||
|  | 			return nil, errC | ||||||
|  | 		} else { | ||||||
|  | 			stmtCache = _stmtCache | ||||||
|  | 			stmtCacheSize = maxStmtCacheSize | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	al := new(alias) | 	al := new(alias) | ||||||
| 	al.Name = aliasName | 	al.Name = aliasName | ||||||
| 	al.DriverName = driverName | 	al.DriverName = driverName | ||||||
| 	al.DB = &DB{ | 	al.DB = &DB{ | ||||||
| 		RWMutex:        new(sync.RWMutex), | 		RWMutex:             new(sync.RWMutex), | ||||||
| 		DB:             db, | 		DB:                  db, | ||||||
| 		stmtDecorators: newStmtDecoratorLruWithEvict(), | 		stmtDecorators:      stmtCache, | ||||||
|  | 		stmtDecoratorsLimit: stmtCacheSize, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if dr, ok := drivers[driverName]; ok { | 	if dr, ok := drivers[driverName]; ok { | ||||||
| @ -371,12 +382,22 @@ func addAliasWthDB(aliasName, driverName string, db *sql.DB) (*alias, error) { | |||||||
| 		return nil, fmt.Errorf("DataBase alias name `%s` already registered, cannot reuse", aliasName) | 		return nil, fmt.Errorf("DataBase alias name `%s` already registered, cannot reuse", aliasName) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	detectTZ(al) | ||||||
|  | 
 | ||||||
|  | 	kvs.IfContains(MaxIdleConnsKey, func(value interface{}) { | ||||||
|  | 		SetMaxIdleConns(al.Name, value.(int)) | ||||||
|  | 	}).IfContains(MaxOpenConnsKey, func(value interface{}) { | ||||||
|  | 		SetMaxOpenConns(al.Name, value.(int)) | ||||||
|  | 	}).IfContains(ConnMaxLifetimeKey, func(value interface{}) { | ||||||
|  | 		SetConnMaxLifetime(al.Name, value.(time.Duration)) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
| 	return al, nil | 	return al, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // AddAliasWthDB add a aliasName for the drivename | // AddAliasWthDB add a aliasName for the drivename | ||||||
| func AddAliasWthDB(aliasName, driverName string, db *sql.DB) error { | func AddAliasWthDB(aliasName, driverName string, db *sql.DB, params ...common.KV) error { | ||||||
| 	_, err := addAliasWthDB(aliasName, driverName, db) | 	_, err := addAliasWthDB(aliasName, driverName, db, params...) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -388,7 +409,6 @@ func RegisterDataBase(aliasName, driverName, dataSource string, params ...common | |||||||
| 		al  *alias | 		al  *alias | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 	kvs := common.NewKVs(params...) |  | ||||||
| 
 | 
 | ||||||
| 	db, err = sql.Open(driverName, dataSource) | 	db, err = sql.Open(driverName, dataSource) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -396,23 +416,13 @@ func RegisterDataBase(aliasName, driverName, dataSource string, params ...common | |||||||
| 		goto end | 		goto end | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	al, err = addAliasWthDB(aliasName, driverName, db) | 	al, err = addAliasWthDB(aliasName, driverName, db, params...) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		goto end | 		goto end | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	al.DataSource = dataSource | 	al.DataSource = dataSource | ||||||
| 
 | 
 | ||||||
| 	detectTZ(al) |  | ||||||
| 
 |  | ||||||
| 	kvs.IfContains(MaxIdleConnsKey, func(value interface{}) { |  | ||||||
| 		SetMaxIdleConns(al.Name, value.(int)) |  | ||||||
| 	}).IfContains(MaxOpenConnsKey, func(value interface{}) { |  | ||||||
| 		SetMaxOpenConns(al.Name, value.(int)) |  | ||||||
| 	}).IfContains(ConnMaxLifetimeKey, func(value interface{}) { |  | ||||||
| 		SetConnMaxLifetime(al.Name, value.(time.Duration)) |  | ||||||
| 	}) |  | ||||||
| 
 |  | ||||||
| end: | end: | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if db != nil { | 		if db != nil { | ||||||
| @ -517,9 +527,12 @@ func newStmtDecorator(sqlStmt *sql.Stmt) *stmtDecorator { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func newStmtDecoratorLruWithEvict() *lru.Cache { | func newStmtDecoratorLruWithEvict(cacheSize int) (*lru.Cache, error) { | ||||||
| 	cache, _ := lru.NewWithEvict(1000, func(key interface{}, value interface{}) { | 	cache, err := lru.NewWithEvict(cacheSize, func(key interface{}, value interface{}) { | ||||||
| 		value.(*stmtDecorator).destroy() | 		value.(*stmtDecorator).destroy() | ||||||
| 	}) | 	}) | ||||||
| 	return cache | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return cache, nil | ||||||
| } | } | ||||||
|  | |||||||
| @ -42,3 +42,56 @@ func TestRegisterDataBase(t *testing.T) { | |||||||
| 	assert.Equal(t, al.MaxOpenConns, 300) | 	assert.Equal(t, al.MaxOpenConns, 300) | ||||||
| 	assert.Equal(t, al.ConnMaxLifetime, time.Minute) | 	assert.Equal(t, al.ConnMaxLifetime, time.Minute) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestRegisterDataBase_MaxStmtCacheSizeNegative1(t *testing.T) { | ||||||
|  | 	aliasName := "TestRegisterDataBase_MaxStmtCacheSizeNegative1" | ||||||
|  | 	err := RegisterDataBase(aliasName, DBARGS.Driver, DBARGS.Source, common.KV{ | ||||||
|  | 		Key:   MaxStmtCacheSize, | ||||||
|  | 		Value: -1, | ||||||
|  | 	}) | ||||||
|  | 	assert.Nil(t, err) | ||||||
|  | 
 | ||||||
|  | 	al := getDbAlias(aliasName) | ||||||
|  | 	assert.NotNil(t, al) | ||||||
|  | 	assert.Equal(t, al.DB.stmtDecoratorsLimit, 0) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestRegisterDataBase_MaxStmtCacheSize0(t *testing.T) { | ||||||
|  | 	aliasName := "TestRegisterDataBase_MaxStmtCacheSize0" | ||||||
|  | 	err := RegisterDataBase(aliasName, DBARGS.Driver, DBARGS.Source, common.KV{ | ||||||
|  | 		Key:   MaxStmtCacheSize, | ||||||
|  | 		Value: 0, | ||||||
|  | 	}) | ||||||
|  | 	assert.Nil(t, err) | ||||||
|  | 
 | ||||||
|  | 	al := getDbAlias(aliasName) | ||||||
|  | 	assert.NotNil(t, al) | ||||||
|  | 	assert.Equal(t, al.DB.stmtDecoratorsLimit, 0) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestRegisterDataBase_MaxStmtCacheSize1(t *testing.T) { | ||||||
|  | 	aliasName := "TestRegisterDataBase_MaxStmtCacheSize1" | ||||||
|  | 	err := RegisterDataBase(aliasName, DBARGS.Driver, DBARGS.Source, common.KV{ | ||||||
|  | 		Key:   MaxStmtCacheSize, | ||||||
|  | 		Value: 1, | ||||||
|  | 	}) | ||||||
|  | 	assert.Nil(t, err) | ||||||
|  | 
 | ||||||
|  | 	al := getDbAlias(aliasName) | ||||||
|  | 	assert.NotNil(t, al) | ||||||
|  | 	assert.Equal(t, al.DB.stmtDecoratorsLimit, 1) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestRegisterDataBase_MaxStmtCacheSize841(t *testing.T) { | ||||||
|  | 	aliasName := "TestRegisterDataBase_MaxStmtCacheSize841" | ||||||
|  | 	err := RegisterDataBase(aliasName, DBARGS.Driver, DBARGS.Source, common.KV{ | ||||||
|  | 		Key:   MaxStmtCacheSize, | ||||||
|  | 		Value: 841, | ||||||
|  | 	}) | ||||||
|  | 	assert.Nil(t, err) | ||||||
|  | 
 | ||||||
|  | 	al := getDbAlias(aliasName) | ||||||
|  | 	assert.NotNil(t, al) | ||||||
|  | 	assert.Equal(t, al.DB.stmtDecoratorsLimit, 841) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | |||||||
| @ -58,6 +58,8 @@ import ( | |||||||
| 	"database/sql" | 	"database/sql" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"github.com/astaxie/beego/pkg/common" | ||||||
|  | 	lru "github.com/hashicorp/golang-lru" | ||||||
| 	"os" | 	"os" | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"sync" | 	"sync" | ||||||
| @ -609,7 +611,7 @@ func NewOrm() Ormer { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewOrmWithDB create a new ormer object with specify *sql.DB for query | // NewOrmWithDB create a new ormer object with specify *sql.DB for query | ||||||
| func NewOrmWithDB(driverName, aliasName string, db *sql.DB) (Ormer, error) { | func NewOrmWithDB(driverName, aliasName string, db *sql.DB, params ...common.KV) (Ormer, error) { | ||||||
| 	var al *alias | 	var al *alias | ||||||
| 
 | 
 | ||||||
| 	if dr, ok := drivers[driverName]; ok { | 	if dr, ok := drivers[driverName]; ok { | ||||||
| @ -620,16 +622,41 @@ func NewOrmWithDB(driverName, aliasName string, db *sql.DB) (Ormer, error) { | |||||||
| 		return nil, fmt.Errorf("driver name `%s` have not registered", driverName) | 		return nil, fmt.Errorf("driver name `%s` have not registered", driverName) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	kvs := common.NewKVs(params...) | ||||||
|  | 
 | ||||||
|  | 	var stmtCache *lru.Cache | ||||||
|  | 	var stmtCacheSize int | ||||||
|  | 
 | ||||||
|  | 	maxStmtCacheSize := kvs.GetValueOr(MaxStmtCacheSize, 0).(int) | ||||||
|  | 	if maxStmtCacheSize > 0 { | ||||||
|  | 		_stmtCache, errC := newStmtDecoratorLruWithEvict(maxStmtCacheSize) | ||||||
|  | 		if errC != nil { | ||||||
|  | 			return nil, errC | ||||||
|  | 		} else { | ||||||
|  | 			stmtCache = _stmtCache | ||||||
|  | 			stmtCacheSize = maxStmtCacheSize | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	al.Name = aliasName | 	al.Name = aliasName | ||||||
| 	al.DriverName = driverName | 	al.DriverName = driverName | ||||||
| 	al.DB = &DB{ | 	al.DB = &DB{ | ||||||
| 		RWMutex:        new(sync.RWMutex), | 		RWMutex:             new(sync.RWMutex), | ||||||
| 		DB:             db, | 		DB:                  db, | ||||||
| 		stmtDecorators: newStmtDecoratorLruWithEvict(), | 		stmtDecorators:      stmtCache, | ||||||
|  | 		stmtDecoratorsLimit: stmtCacheSize, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	detectTZ(al) | 	detectTZ(al) | ||||||
| 
 | 
 | ||||||
|  | 	kvs.IfContains(MaxIdleConnsKey, func(value interface{}) { | ||||||
|  | 		SetMaxIdleConns(al.Name, value.(int)) | ||||||
|  | 	}).IfContains(MaxOpenConnsKey, func(value interface{}) { | ||||||
|  | 		SetMaxOpenConns(al.Name, value.(int)) | ||||||
|  | 	}).IfContains(ConnMaxLifetimeKey, func(value interface{}) { | ||||||
|  | 		SetConnMaxLifetime(al.Name, value.(time.Duration)) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
| 	o := new(orm) | 	o := new(orm) | ||||||
| 	o.alias = al | 	o.alias = al | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user