mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-13 00:13:33 +08:00
🐛 fix(db): 优化 SQLServer 首次查询耗时
- 为 SQLServer 连接池保留 1 条空闲连接,减少反复冷连接开销 - 复用 Oracle/OceanBase 的默认空闲连接常量,其他默认数据库池策略保持不变 - 新增连接池行为测试,覆盖 SQLServer 复用与默认池关闭 idle 连接
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
const (
|
||||
defaultSQLMaxOpenConns = 4
|
||||
defaultSQLMaxIdleConns = 1
|
||||
defaultSQLConnMaxLifetime = 30 * time.Minute
|
||||
defaultSQLConnMaxIdleTime = 30 * time.Second
|
||||
)
|
||||
@@ -21,7 +22,13 @@ func configureSQLConnectionPool(db *sql.DB, dbType string) {
|
||||
return
|
||||
case "oracle", "oceanbase":
|
||||
db.SetMaxOpenConns(defaultSQLMaxOpenConns)
|
||||
db.SetMaxIdleConns(1)
|
||||
db.SetMaxIdleConns(defaultSQLMaxIdleConns)
|
||||
db.SetConnMaxIdleTime(defaultSQLConnMaxIdleTime)
|
||||
db.SetConnMaxLifetime(defaultSQLConnMaxLifetime)
|
||||
return
|
||||
case "sqlserver":
|
||||
db.SetMaxOpenConns(defaultSQLMaxOpenConns)
|
||||
db.SetMaxIdleConns(defaultSQLMaxIdleConns)
|
||||
db.SetConnMaxIdleTime(defaultSQLConnMaxIdleTime)
|
||||
db.SetConnMaxLifetime(defaultSQLConnMaxLifetime)
|
||||
return
|
||||
|
||||
102
internal/db/sql_pool_test.go
Normal file
102
internal/db/sql_pool_test.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const poolRecordingDriverName = "gonavi_pool_recording"
|
||||
|
||||
var (
|
||||
poolRecordingOpenCount atomic.Int64
|
||||
poolRecordingCloseCount atomic.Int64
|
||||
)
|
||||
|
||||
func init() {
|
||||
sql.Register(poolRecordingDriverName, poolRecordingDriver{})
|
||||
}
|
||||
|
||||
type poolRecordingDriver struct{}
|
||||
|
||||
func (poolRecordingDriver) Open(name string) (driver.Conn, error) {
|
||||
poolRecordingOpenCount.Add(1)
|
||||
return poolRecordingConn{}, nil
|
||||
}
|
||||
|
||||
type poolRecordingConn struct{}
|
||||
|
||||
func (poolRecordingConn) Prepare(query string) (driver.Stmt, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (poolRecordingConn) Close() error {
|
||||
poolRecordingCloseCount.Add(1)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (poolRecordingConn) Begin() (driver.Tx, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (poolRecordingConn) Ping(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func resetPoolRecordingDriverCounters() {
|
||||
poolRecordingOpenCount.Store(0)
|
||||
poolRecordingCloseCount.Store(0)
|
||||
}
|
||||
|
||||
func openConfiguredPoolForTest(t *testing.T, dbType string) *sql.DB {
|
||||
t.Helper()
|
||||
resetPoolRecordingDriverCounters()
|
||||
dbConn, err := sql.Open(poolRecordingDriverName, t.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("sql.Open failed: %v", err)
|
||||
}
|
||||
configureSQLConnectionPool(dbConn, dbType)
|
||||
t.Cleanup(func() {
|
||||
_ = dbConn.Close()
|
||||
})
|
||||
return dbConn
|
||||
}
|
||||
|
||||
func TestConfigureSQLConnectionPoolKeepsOneIdleSQLServerConnection(t *testing.T) {
|
||||
dbConn := openConfiguredPoolForTest(t, "sqlserver")
|
||||
|
||||
if err := dbConn.PingContext(context.Background()); err != nil {
|
||||
t.Fatalf("first ping failed: %v", err)
|
||||
}
|
||||
if err := dbConn.PingContext(context.Background()); err != nil {
|
||||
t.Fatalf("second ping failed: %v", err)
|
||||
}
|
||||
|
||||
if got := poolRecordingOpenCount.Load(); got != 1 {
|
||||
t.Fatalf("expected SQL Server pool to reuse one idle connection, opened %d connections", got)
|
||||
}
|
||||
if got := poolRecordingCloseCount.Load(); got != 0 {
|
||||
t.Fatalf("expected SQL Server idle connection to remain cached before DB close, closed %d connections", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigureSQLConnectionPoolDefaultDoesNotKeepIdleConnections(t *testing.T) {
|
||||
dbConn := openConfiguredPoolForTest(t, "mysql")
|
||||
|
||||
if err := dbConn.PingContext(context.Background()); err != nil {
|
||||
t.Fatalf("first ping failed: %v", err)
|
||||
}
|
||||
if err := dbConn.PingContext(context.Background()); err != nil {
|
||||
t.Fatalf("second ping failed: %v", err)
|
||||
}
|
||||
|
||||
if got := poolRecordingOpenCount.Load(); got != 2 {
|
||||
t.Fatalf("expected default pool to reopen without idle cache, opened %d connections", got)
|
||||
}
|
||||
if got := poolRecordingCloseCount.Load(); got != 2 {
|
||||
t.Fatalf("expected default pool to close each returned connection, closed %d connections", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user