refactor: pool get options支持不创建连接

This commit is contained in:
meilin.huang
2025-05-29 20:24:48 +08:00
parent 42fbfd3c47
commit 7a17042276
15 changed files with 59 additions and 59 deletions

View File

@@ -13,7 +13,7 @@
"@element-plus/icons-vue": "^2.3.1", "@element-plus/icons-vue": "^2.3.1",
"@logicflow/core": "^2.0.13", "@logicflow/core": "^2.0.13",
"@logicflow/extension": "^2.0.18", "@logicflow/extension": "^2.0.18",
"@vueuse/core": "^13.2.0", "@vueuse/core": "^13.3.0",
"@xterm/addon-fit": "^0.10.0", "@xterm/addon-fit": "^0.10.0",
"@xterm/addon-search": "^0.15.0", "@xterm/addon-search": "^0.15.0",
"@xterm/addon-web-links": "^0.11.0", "@xterm/addon-web-links": "^0.11.0",
@@ -36,12 +36,12 @@
"qrcode.vue": "^3.6.0", "qrcode.vue": "^3.6.0",
"screenfull": "^6.0.2", "screenfull": "^6.0.2",
"sortablejs": "^1.15.6", "sortablejs": "^1.15.6",
"splitpanes": "^4.0.3", "splitpanes": "^4.0.4",
"sql-formatter": "^15.6.1", "sql-formatter": "^15.6.1",
"trzsz": "^1.1.5", "trzsz": "^1.1.5",
"uuid": "^9.0.1", "uuid": "^9.0.1",
"vue": "^3.5.14", "vue": "^3.5.16",
"vue-i18n": "^11.1.3", "vue-i18n": "^11.1.5",
"vue-router": "^4.5.1", "vue-router": "^4.5.1",
"vuedraggable": "^4.1.0" "vuedraggable": "^4.1.0"
}, },
@@ -54,16 +54,16 @@
"@typescript-eslint/eslint-plugin": "^6.7.4", "@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4", "@typescript-eslint/parser": "^6.7.4",
"@vitejs/plugin-vue": "^5.2.4", "@vitejs/plugin-vue": "^5.2.4",
"@vue/compiler-sfc": "^3.5.14", "@vue/compiler-sfc": "^3.5.16",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"code-inspector-plugin": "^0.20.9", "code-inspector-plugin": "^0.20.9",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"eslint": "^9.25.1", "eslint": "^9.27.0",
"eslint-plugin-vue": "^10.0.0", "eslint-plugin-vue": "^10.1.0",
"postcss": "^8.5.3", "postcss": "^8.5.4",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"sass": "^1.89.0", "sass": "^1.89.0",
"tailwindcss": "^4.1.7", "tailwindcss": "^4.1.8",
"typescript": "^5.8.2", "typescript": "^5.8.2",
"vite": "^6.3.5", "vite": "^6.3.5",
"vite-plugin-progress": "0.0.7", "vite-plugin-progress": "0.0.7",

View File

@@ -19,7 +19,7 @@ type DataSyncTaskListVO struct {
type DataSyncLogListVO struct { type DataSyncLogListVO struct {
CreateTime *time.Time `json:"createTime"` CreateTime *time.Time `json:"createTime"`
DataSqlFull string `json:"dataSqlFull"` DataSqlFull string `json:"dataSqlFull"`
ResNum string `json:"resNum"` ResNum int `json:"resNum"`
ErrText string `json:"errText"` ErrText string `json:"errText"`
Status *int `json:"status"` Status *int `json:"status"`
} }

View File

@@ -17,16 +17,6 @@ func InitIoc() {
func Init() { func Init() {
sync.OnceFunc(func() { sync.OnceFunc(func() {
//if err := GetDbBackupApp().Init(); err != nil {
// panic(fmt.Sprintf("初始化 DbBackupApp 失败: %v", err))
//}
//if err := GetDbRestoreApp().Init(); err != nil {
// panic(fmt.Sprintf("初始化 DbRestoreApp 失败: %v", err))
//}
//if err := GetDbBinlogApp().Init(); err != nil {
// panic(fmt.Sprintf("初始化 DbBinlogApp 失败: %v", err))
//}
GetDataSyncTaskApp().InitCronJob() GetDataSyncTaskApp().InitCronJob()
GetDbTransferTaskApp().InitCronJob() GetDbTransferTaskApp().InitCronJob()
GetDbTransferTaskApp().TimerDeleteTransferFile() GetDbTransferTaskApp().TimerDeleteTransferFile()

View File

@@ -76,6 +76,7 @@ func (app *dataSyncAppImpl) Save(ctx context.Context, taskEntity *entity.DataSyn
taskEntity.TaskKey = uuid.New().String() taskEntity.TaskKey = uuid.New().String()
err = app.Insert(ctx, taskEntity) err = app.Insert(ctx, taskEntity)
} else { } else {
taskEntity.TaskKey = ""
err = app.UpdateById(ctx, taskEntity) err = app.UpdateById(ctx, taskEntity)
} }
@@ -107,15 +108,13 @@ func (app *dataSyncAppImpl) AddCronJob(ctx context.Context, taskEntity *entity.D
// 根据状态添加新的任务 // 根据状态添加新的任务
if taskEntity.Status == entity.DataSyncTaskStatusEnable { if taskEntity.Status == entity.DataSyncTaskStatusEnable {
taskId := taskEntity.Id taskId := taskEntity.Id
logx.Infof("start add the data sync task job: %s, cron[%s]", taskEntity.TaskName, taskEntity.TaskCron)
if err := scheduler.AddFunByKey(key, taskEntity.TaskCron, func() { if err := scheduler.AddFunByKey(key, taskEntity.TaskCron, func() {
logx.Infof("start the data synchronization task: %d", taskId) if err := app.RunCronJob(context.Background(), taskId); err != nil {
cancelCtx, cancelFunc := context.WithCancel(ctx) logx.Errorf("the data sync task failed to execute at a scheduled time: %s", err.Error())
defer cancelFunc()
if err := app.RunCronJob(cancelCtx, taskId); err != nil {
logx.Errorf("the data synchronization task failed to execute at a scheduled time: %s", err.Error())
} }
}); err != nil { }); err != nil {
logx.ErrorTrace("add db data sync cron job failed", err) logx.ErrorTrace("add db data sync job failed", err)
} }
} }
} }
@@ -133,6 +132,9 @@ func (app *dataSyncAppImpl) RunCronJob(ctx context.Context, id uint64) error {
if err != nil { if err != nil {
return errorx.NewBiz("task not found") return errorx.NewBiz("task not found")
} }
logx.InfofContext(ctx, "start the data sync task: %s => %s", task.TaskName, task.TaskKey)
if task.RunningState == entity.DataSyncTaskRunStateRunning { if task.RunningState == entity.DataSyncTaskRunStateRunning {
return errorx.NewBiz("the task is in progress") return errorx.NewBiz("the task is in progress")
} }
@@ -140,8 +142,6 @@ func (app *dataSyncAppImpl) RunCronJob(ctx context.Context, id uint64) error {
// 标记该任务运行中 // 标记该任务运行中
app.MarkRunning(id) app.MarkRunning(id)
logx.InfofContext(ctx, "start the data synchronization task: %s => %s", task.TaskName, task.TaskKey)
go func() { go func() {
// 通过占位符格式化sql // 通过占位符格式化sql
updSql := "" updSql := ""

View File

@@ -22,10 +22,7 @@ func init() {
mcm.AddCheckSshTunnelMachineUseFunc(func(machineId int) bool { mcm.AddCheckSshTunnelMachineUseFunc(func(machineId int) bool {
items := poolGroup.AllPool() items := poolGroup.AllPool()
for _, v := range items { for _, v := range items {
if v.Stats().TotalConns == 0 { conn, err := v.Get(context.Background(), pool.WithGetNoUpdateLastActive(), pool.WithGetNoNewConn())
continue // 连接池中没有连接,跳过
}
conn, err := v.Get(context.Background(), pool.WithNoUpdateLastActive())
if err != nil { if err != nil {
continue // 获取连接失败,跳过 continue // 获取连接失败,跳过
} }

View File

@@ -45,10 +45,7 @@ func init() {
mcm.AddCheckSshTunnelMachineUseFunc(func(machineId int) bool { mcm.AddCheckSshTunnelMachineUseFunc(func(machineId int) bool {
items := poolGroup.AllPool() items := poolGroup.AllPool()
for _, v := range items { for _, v := range items {
if v.Stats().TotalConns == 0 { conn, err := v.Get(context.Background(), pool.WithGetNoUpdateLastActive(), pool.WithGetNoNewConn())
continue // 连接池中没有连接,跳过
}
conn, err := v.Get(context.Background(), pool.WithNoUpdateLastActive())
if err != nil { if err != nil {
continue // 获取连接失败,跳过 continue // 获取连接失败,跳过
} }

View File

@@ -18,7 +18,7 @@ import (
type EsVersion string type EsVersion string
type EsInfo struct { type EsInfo struct {
model.ExtraData // 连接需要的其他额外参数json字符串如oracle数据库需要指定sid等 model.ExtraData // 连接需要的其他额外参数json字符串
InstanceId uint64 // 实例id InstanceId uint64 // 实例id
Name string Name string

View File

@@ -53,10 +53,7 @@ func GetMachineCli(ctx context.Context, authCertName string, getMachine func(str
// 删除指定机器缓存客户端,并关闭客户端连接 // 删除指定机器缓存客户端,并关闭客户端连接
func DeleteCli(id uint64) { func DeleteCli(id uint64) {
for _, p := range poolGroup.AllPool() { for _, p := range poolGroup.AllPool() {
if p.Stats().TotalConns == 0 { conn, err := p.Get(context.Background(), pool.WithGetNoUpdateLastActive(), pool.WithGetNoNewConn())
continue
}
conn, err := p.Get(context.Background(), pool.WithNoUpdateLastActive())
if err != nil { if err != nil {
continue continue
} }

View File

@@ -14,10 +14,7 @@ func init() {
mcm.AddCheckSshTunnelMachineUseFunc(func(machineId int) bool { mcm.AddCheckSshTunnelMachineUseFunc(func(machineId int) bool {
items := poolGroup.AllPool() items := poolGroup.AllPool()
for _, v := range items { for _, v := range items {
if v.Stats().TotalConns == 0 { conn, err := v.Get(context.Background(), pool.WithGetNoUpdateLastActive(), pool.WithGetNoNewConn())
continue // 连接池中没有连接,跳过
}
conn, err := v.Get(context.Background(), pool.WithNoUpdateLastActive())
if err != nil { if err != nil {
continue // 获取连接失败,跳过 continue // 获取连接失败,跳过
} }

View File

@@ -174,8 +174,7 @@ func (r *redisAppImpl) Delete(ctx context.Context, id uint64) error {
} }
// 如果存在连接,则关闭所有库连接信息 // 如果存在连接,则关闭所有库连接信息
for _, dbStr := range strings.Split(re.Db, ",") { for _, dbStr := range strings.Split(re.Db, ",") {
db, _ := strconv.Atoi(dbStr) rdm.CloseConn(re.Id, cast.ToInt(dbStr))
rdm.CloseConn(re.Id, db)
} }
return r.Tx(ctx, func(ctx context.Context) error { return r.Tx(ctx, func(ctx context.Context) error {

View File

@@ -15,10 +15,7 @@ func init() {
// 遍历所有redis连接实例若存在redis实例使用该ssh隧道机器则返回true表示还在使用中... // 遍历所有redis连接实例若存在redis实例使用该ssh隧道机器则返回true表示还在使用中...
items := poolGroup.AllPool() items := poolGroup.AllPool()
for _, v := range items { for _, v := range items {
if v.Stats().TotalConns == 0 { rc, err := v.Get(context.Background(), pool.WithGetNoUpdateLastActive(), pool.WithGetNoNewConn())
continue // 连接池中没有连接,跳过
}
rc, err := v.Get(context.Background(), pool.WithNoUpdateLastActive())
if err != nil { if err != nil {
continue // 获取连接失败,跳过 continue // 获取连接失败,跳过
} }

View File

@@ -49,7 +49,7 @@ func NewCachePool[T Conn](factory func() (T, error), opts ...Option[T]) *CachePo
func (p *CachePool[T]) Get(ctx context.Context, opts ...GetOption) (T, error) { func (p *CachePool[T]) Get(ctx context.Context, opts ...GetOption) (T, error) {
var zero T var zero T
options := getOptions{updateLastActive: true} // 默认更新 lastActive options := defaultGetOptions // 默认更新 lastActive
for _, apply := range opts { for _, apply := range opts {
apply(&options) apply(&options)
} }
@@ -75,6 +75,10 @@ func (p *CachePool[T]) Get(ctx context.Context, opts ...GetOption) (T, error) {
} }
p.mu.RUnlock() p.mu.RUnlock()
if !options.newConn {
return zero, ErrNoAvailableConn
}
// 没有找到可用连接,升级为写锁进行创建 // 没有找到可用连接,升级为写锁进行创建
p.mu.Lock() p.mu.Lock()
defer p.mu.Unlock() defer p.mu.Unlock()
@@ -241,6 +245,7 @@ func (p *CachePool[T]) ping(conn T) bool {
case <-done: case <-done:
return result return result
case <-time.After(2 * time.Second): // 设置超时 case <-time.After(2 * time.Second): // 设置超时
logx.Debug("ping timeout")
return false // 超时认为不可用 return false // 超时认为不可用
} }
} }

View File

@@ -78,7 +78,7 @@ func (p *ChanPool[T]) Get(ctx context.Context, opts ...GetOption) (T, error) {
connChan := make(chan T, 1) connChan := make(chan T, 1)
errChan := make(chan error, 1) errChan := make(chan error, 1)
options := getOptions{updateLastActive: true} // 默认更新 lastActive options := defaultGetOptions // 默认更新 lastActive
for _, apply := range opts { for _, apply := range opts {
apply(&options) apply(&options)
} }
@@ -114,11 +114,11 @@ func (p *ChanPool[T]) Get(ctx context.Context, opts ...GetOption) (T, error) {
} }
func (p *ChanPool[T]) get(opts getOptions) (T, error) { func (p *ChanPool[T]) get(opts getOptions) (T, error) {
var zero T
// 检查连接池是否已关闭 // 检查连接池是否已关闭
p.mu.RLock() p.mu.RLock()
if p.closed { if p.closed {
p.mu.RUnlock() p.mu.RUnlock()
var zero T
return zero, ErrPoolClosed return zero, ErrPoolClosed
} }
p.mu.RUnlock() p.mu.RUnlock()
@@ -133,6 +133,9 @@ func (p *ChanPool[T]) get(opts getOptions) (T, error) {
} }
return wrapper.conn, nil return wrapper.conn, nil
default: default:
if !opts.newConn {
return zero, ErrNoAvailableConn
}
return p.createConn() return p.createConn()
} }
} }

View File

@@ -5,7 +5,10 @@ import (
"time" "time"
) )
var ErrPoolClosed = errors.New("pool is closed") var (
ErrPoolClosed = errors.New("pool is closed")
ErrNoAvailableConn = errors.New("no available connection")
)
// PoolConfig 连接池配置 // PoolConfig 连接池配置
type PoolConfig[T Conn] struct { type PoolConfig[T Conn] struct {
@@ -71,11 +74,26 @@ type GetOption func(*getOptions)
// 控制 Get 行为的选项 // 控制 Get 行为的选项
type getOptions struct { type getOptions struct {
updateLastActive bool // 是否更新 lastActive默认 true updateLastActive bool // 是否更新 lastActive默认 true
newConn bool // 连接不存在时是否创建新连接,默认 true
} }
// WithNoUpdateLastActive 返回一个 Option禁用更新 lastActive var (
func WithNoUpdateLastActive() GetOption { defaultGetOptions = getOptions{
updateLastActive: true,
newConn: true,
}
)
// WithGetNoUpdateLastActive 返回一个 Option禁用更新 lastActive
func WithGetNoUpdateLastActive() GetOption {
return func(o *getOptions) { return func(o *getOptions) {
o.updateLastActive = false o.updateLastActive = false
} }
} }
// WithGetNoCreateConn 禁用获取时连接不存在创建连接
func WithGetNoNewConn() GetOption {
return func(o *getOptions) {
o.newConn = false
}
}

View File

@@ -117,8 +117,8 @@ func (pg *PoolGroup[T]) asyncClose(pool Pool[T], key string) {
select { select {
case <-done: case <-done:
logx.Infof("pool group - pool closed successfully, key: %s", key) logx.Infof("pool group - pool closed successfully, key: %s", key)
case <-time.After(5 * time.Second): case <-time.After(10 * time.Second):
logx.Errorf("pool group - pool close timeout, possible deadlock detected, key: %s", key) logx.Errorf("pool group - pool close timeout, key: %s", key)
// 打印当前 goroutine 的堆栈信息 // 打印当前 goroutine 的堆栈信息
buf := make([]byte, 1<<16) buf := make([]byte, 1<<16)
runtime.Stack(buf, true) runtime.Stack(buf, true)