️ perf(kingbase): 消除 SQL 查询周期性探活延迟

- 成功 SQL 往返后刷新缓存连接健康时间
- 避免活跃连接跨过旧时间边界时同步重复 Ping
- 保留空闲连接探活、失效清理与重建机制
- 新增 Kingbase 查询健康时间回归测试
This commit is contained in:
Syngnat
2026-07-30 14:32:52 +08:00
parent 8a9b85518a
commit b64c7e92d1
3 changed files with 74 additions and 0 deletions

View File

@@ -660,6 +660,25 @@ func (a *App) databaseConnectionReturnError(cacheKey string, inst db.Database) e
return nil
}
func (a *App) markCachedDatabaseHealthy(inst db.Database, healthyAt time.Time) {
if a == nil || inst == nil {
return
}
if healthyAt.IsZero() {
healthyAt = time.Now()
}
a.mu.Lock()
defer a.mu.Unlock()
for key, entry := range a.dbCache {
if entry.inst != inst || !healthyAt.After(entry.lastPing) {
continue
}
entry.lastPing = healthyAt
a.dbCache[key] = entry
}
}
func (a *App) cancelDatabaseConnectFlightsLocked(match func(*databaseConnectFlight) bool, cancelErr error, excludedFlightID uint64) []string {
groupKeys := make([]string, 0)
for _, flight := range a.dbConnectFlights {

View File

@@ -1144,6 +1144,7 @@ func (a *App) dbQueryMulti(
// 慢 SQL 埋点:成功执行后记录(低于阈值 500ms 自动跳过)。
// 用 named return + defer 覆盖所有 return path避免遗漏。
var queryExecutionDuration time.Duration
queryExecuted := false
defer func() {
if !result.Success {
return
@@ -1153,6 +1154,7 @@ func (a *App) dbQueryMulti(
}()
measureQueryExecution := func(run func()) {
startedAt := time.Now()
queryExecuted = true
run()
queryExecutionDuration += time.Since(startedAt)
}
@@ -1190,6 +1192,12 @@ func (a *App) dbQueryMulti(
logger.Error(err, "DBQueryMulti 获取连接失败:%s", formatConnSummary(runConfig))
return connection.QueryResult{Success: false, Message: err.Error(), QueryID: queryID}
}
defer func() {
// A successful SQL round trip is at least as strong a health signal as Ping.
if result.Success && queryExecuted {
a.markCachedDatabaseHealthy(dbInst, time.Now())
}
}()
ctx, cancel := newQueryExecutionContext(runConfig)
defer cancel()
@@ -1298,6 +1306,7 @@ func (a *App) dbQueryMulti(
logger.Error(retryErr, "DBQueryMulti 重建连接失败:%s SQL片段=%q", formatConnSummary(runConfig), sqlSnippet(query))
return connection.QueryResult{Success: false, Message: retryErr.Error(), QueryID: queryID}
}
dbInst = retryInst
results, resultMessages, err = runMultiQuery(retryInst)
}
}
@@ -1411,6 +1420,7 @@ func (a *App) dbQueryMulti(
logger.Error(retryErr, "DBQueryMulti 批量写重建连接失败:%s", formatConnSummary(runConfig))
return connection.QueryResult{Success: false, Message: retryErr.Error(), QueryID: queryID}
}
dbInst = retryInst
if retryBatcher, ok2 := retryInst.(db.BatchWriteExecer); ok2 {
measureQueryExecution(func() {
affected, batchErr = retryBatcher.ExecBatchContext(ctx, query)

View File

@@ -17,6 +17,7 @@ import (
type fakeBatchWriteDB struct {
batchCalls int
execCalls int
pingCalls int
execQueries []string
lastQuery string
lastCtx context.Context
@@ -114,6 +115,7 @@ func (f *fakeBatchWriteDB) Close() error {
}
func (f *fakeBatchWriteDB) Ping() error {
f.pingCalls++
return nil
}
@@ -2318,6 +2320,49 @@ func TestDBQueryMultiPrefersPlainQueryForKingbaseReadResults(t *testing.T) {
}
}
func TestDBQueryMultiSuccessfulKingbaseQueryRefreshesCachedHealthTimestamp(t *testing.T) {
installFakeOptionalDriverRuntime(t)
query := "SELECT * FROM ldf_server.andon_dash_events LIMIT 101 OFFSET 0"
fakeDB := &fakeBatchWriteDB{
queryMap: map[string][]map[string]interface{}{
query: {
{"id": 1},
},
},
fieldMap: map[string][]string{
query: {"id"},
},
queryErr: map[string]error{},
}
app := NewAppWithSecretStore(secretstore.NewUnavailableStore("test"))
config := connection.ConnectionConfig{
Type: "kingbase",
Host: "127.0.0.1",
Port: 54321,
User: "system",
Database: "ldf_server_dbs_dev",
}
key := getCacheKey(config)
previousHealthyAt := time.Now().Add(-10 * time.Second)
app.dbCache[key] = cachedDatabase{
inst: fakeDB,
lastPing: previousHealthyAt,
config: normalizeCacheKeyConfig(config),
}
result := app.DBQueryMulti(config, config.Database, query, "kingbase-refresh-cache-health-test")
if !result.Success {
t.Fatalf("expected DBQueryMulti success, got failure: %s", result.Message)
}
if fakeDB.pingCalls != 0 {
t.Fatalf("expected a recently healthy cached connection to skip foreground Ping, got %d calls", fakeDB.pingCalls)
}
if got := app.dbCache[key].lastPing; !got.After(previousHealthyAt) {
t.Fatalf("expected successful query to refresh cached health timestamp, before=%s after=%s", previousHealthyAt, got)
}
}
func TestDBQueryMultiPrefersPlainQueryForDamengReadResults(t *testing.T) {
originalNewDatabaseFunc := newDatabaseFunc
t.Cleanup(func() {