From 5bf68727fa43885a6340ebac464fdd79ffe9495d Mon Sep 17 00:00:00 2001 From: richjohnrambo Date: Sun, 27 Sep 2026 18:36:00 +0800 Subject: [PATCH] fix: cap mysql connection pool --- configs/workbench.local.json | 4 +++- internal/config/config.go | 18 ++++++++++++++++-- internal/config/config_test.go | 15 +++++++++++++++ internal/store/mysql.go | 12 ++++++------ 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/configs/workbench.local.json b/configs/workbench.local.json index 73a94b1..6844178 100644 --- a/configs/workbench.local.json +++ b/configs/workbench.local.json @@ -1,7 +1,9 @@ { "listen": ":6061", "mysql": { - "dsn": "root:root@tcp(127.0.0.1:3306)/meeting_im?parseTime=true&charset=utf8mb4,utf8&loc=Local" + "dsn": "root:root@tcp(127.0.0.1:3306)/meeting_im?parseTime=true&charset=utf8mb4,utf8&loc=Local", + "max_open_conns": 15, + "max_idle_conns": 5 }, "redis": { "addr": "127.0.0.1:6379", diff --git a/internal/config/config.go b/internal/config/config.go index 5d5cdef..a4f1c8d 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -24,7 +24,9 @@ type Config struct { } type MySQLConfig struct { - DSN string `json:"dsn"` + DSN string `json:"dsn"` + MaxOpenConns int `json:"max_open_conns"` + MaxIdleConns int `json:"max_idle_conns"` } type RedisConfig struct { @@ -90,7 +92,9 @@ func defaultConfig() *Config { return &Config{ Listen: ":6061", MySQL: MySQLConfig{ - DSN: "root:root@tcp(127.0.0.1:3306)/meeting_im?parseTime=true&charset=utf8mb4,utf8&loc=Local", + DSN: "root:root@tcp(127.0.0.1:3306)/meeting_im?parseTime=true&charset=utf8mb4,utf8&loc=Local", + MaxOpenConns: 15, + MaxIdleConns: 5, }, Redis: RedisConfig{ Addr: "127.0.0.1:6379", @@ -120,6 +124,8 @@ func defaultConfig() *Config { func applyEnv(cfg *Config) { setString(&cfg.Listen, "IM_WORKBENCH_LISTEN") setString(&cfg.MySQL.DSN, "IM_WORKBENCH_MYSQL_DSN") + setInt(&cfg.MySQL.MaxOpenConns, "IM_WORKBENCH_MYSQL_MAX_OPEN_CONNS") + setInt(&cfg.MySQL.MaxIdleConns, "IM_WORKBENCH_MYSQL_MAX_IDLE_CONNS") setString(&cfg.Redis.Addr, "IM_WORKBENCH_REDIS_ADDR") setString(&cfg.Redis.Username, "IM_WORKBENCH_REDIS_USERNAME") setString(&cfg.Redis.Password, "IM_WORKBENCH_REDIS_PASSWORD") @@ -214,6 +220,14 @@ func setString(target *string, key string) { } } +func setInt(target *int, key string) { + if value := strings.TrimSpace(os.Getenv(key)); value != "" { + if parsed, err := strconv.Atoi(value); err == nil { + *target = parsed + } + } +} + func setBool(target *bool, key string) { switch strings.ToLower(strings.TrimSpace(os.Getenv(key))) { case "1", "true", "yes", "on": diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 25f1209..2a73efd 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -23,4 +23,19 @@ func TestDefaultConfigUsesMeetingIMDatabase(t *testing.T) { if !strings.Contains(cfg.MySQL.DSN, "/meeting_im?") { t.Fatalf("default mysql dsn = %q, want meeting_im database", cfg.MySQL.DSN) } + if cfg.MySQL.MaxOpenConns != 15 || cfg.MySQL.MaxIdleConns != 5 { + t.Fatalf("default mysql pool = %d/%d, want 15/5", cfg.MySQL.MaxOpenConns, cfg.MySQL.MaxIdleConns) + } +} + +func TestApplyEnvOverridesMySQLPool(t *testing.T) { + t.Setenv("IM_WORKBENCH_MYSQL_MAX_OPEN_CONNS", "12") + t.Setenv("IM_WORKBENCH_MYSQL_MAX_IDLE_CONNS", "4") + cfg := defaultConfig() + + applyEnv(cfg) + + if cfg.MySQL.MaxOpenConns != 12 || cfg.MySQL.MaxIdleConns != 4 { + t.Fatalf("mysql pool = %d/%d, want 12/4", cfg.MySQL.MaxOpenConns, cfg.MySQL.MaxIdleConns) + } } diff --git a/internal/store/mysql.go b/internal/store/mysql.go index 72ded44..1048e30 100644 --- a/internal/store/mysql.go +++ b/internal/store/mysql.go @@ -58,7 +58,7 @@ func SetUIDKey(encoded string) error { } func Open(cfg config.MySQLConfig) (*Store, error) { - st, err := openDSN(cfg.DSN) + st, err := openDSN(cfg) if err != nil { return nil, err } @@ -66,13 +66,13 @@ func Open(cfg config.MySQLConfig) (*Store, error) { return st, nil } -func openDSN(dsn string) (*Store, error) { - db, err := sqlx.Open("mysql", dsn) +func openDSN(cfg config.MySQLConfig) (*Store, error) { + db, err := sqlx.Open("mysql", cfg.DSN) if err != nil { return nil, err } - db.SetMaxOpenConns(30) - db.SetMaxIdleConns(10) + db.SetMaxOpenConns(cfg.MaxOpenConns) + db.SetMaxIdleConns(cfg.MaxIdleConns) db.SetConnMaxLifetime(30 * time.Minute) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() @@ -80,7 +80,7 @@ func openDSN(dsn string) (*Store, error) { _ = db.Close() return nil, err } - return &Store{db: db, dsn: dsn}, nil + return &Store{db: db, dsn: cfg.DSN}, nil } func (s *Store) tenant() int64 {