Skip to content

Commit 1905190

Browse files
author
Tew
authored
feat: implement mysql and postgresql store for resources (#1360)
* feat: implement mysql and postgresql store for resources * fix some issues * ut: add some test cases * fix: dynamic table name
1 parent 42d4561 commit 1905190

12 files changed

Lines changed: 3257 additions & 5 deletions

File tree

go.mod

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ go 1.23.0
2020
require (
2121
github.com/Masterminds/semver/v3 v3.2.1
2222
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
23-
github.com/bakito/go-log-logr-adapter v0.0.2
2423
github.com/dubbogo/gost v1.14.0
2524
github.com/duke-git/lancet/v2 v2.3.6
2625
github.com/emicklei/go-restful/v3 v3.11.0
@@ -55,6 +54,10 @@ require (
5554
google.golang.org/grpc v1.73.0
5655
google.golang.org/protobuf v1.36.6
5756
gopkg.in/natefinch/lumberjack.v2 v2.2.1
57+
gorm.io/driver/mysql v1.6.0
58+
gorm.io/driver/postgres v1.6.0
59+
gorm.io/driver/sqlite v1.5.7
60+
gorm.io/gorm v1.30.0
5861
k8s.io/api v0.32.0
5962
k8s.io/apimachinery v0.32.0
6063
k8s.io/client-go v0.32.0
@@ -65,6 +68,7 @@ require (
6568

6669
require (
6770
cel.dev/expr v0.23.0 // indirect
71+
filippo.io/edwards25519 v1.1.0 // indirect
6872
github.com/beorn7/perks v1.0.1 // indirect
6973
github.com/bufbuild/protocompile v0.10.0 // indirect
7074
github.com/bytedance/sonic v1.13.2 // indirect
@@ -85,6 +89,7 @@ require (
8589
github.com/go-playground/locales v0.14.1 // indirect
8690
github.com/go-playground/universal-translator v0.18.1 // indirect
8791
github.com/go-playground/validator/v10 v10.26.0 // indirect
92+
github.com/go-sql-driver/mysql v1.8.1 // indirect
8893
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
8994
github.com/goccy/go-json v0.10.5 // indirect
9095
github.com/gogo/protobuf v1.3.2 // indirect
@@ -95,12 +100,19 @@ require (
95100
github.com/gorilla/securecookie v1.1.2 // indirect
96101
github.com/gorilla/sessions v1.4.0 // indirect
97102
github.com/inconshreveable/mousetrap v1.1.0 // indirect
103+
github.com/jackc/pgpassfile v1.0.0 // indirect
104+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
105+
github.com/jackc/pgx/v5 v5.6.0 // indirect
106+
github.com/jackc/puddle/v2 v2.2.2 // indirect
107+
github.com/jinzhu/inflection v1.0.0 // indirect
108+
github.com/jinzhu/now v1.1.5 // indirect
98109
github.com/josharian/intern v1.0.0 // indirect
99110
github.com/json-iterator/go v1.1.12 // indirect
100111
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
101112
github.com/leodido/go-urn v1.4.0 // indirect
102113
github.com/mailru/easyjson v0.7.7 // indirect
103114
github.com/mattn/go-isatty v0.0.20 // indirect
115+
github.com/mattn/go-sqlite3 v1.14.22 // indirect
104116
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
105117
github.com/modern-go/reflect2 v1.0.2 // indirect
106118
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect

go.sum

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
3434
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
3535
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
3636
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
37+
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
38+
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
3739
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
3840
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
3941
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
@@ -51,8 +53,6 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV
5153
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
5254
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
5355
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
54-
github.com/bakito/go-log-logr-adapter v0.0.2 h1:epK+VaMPkK7dK+Vs78xo0BABqN1lIXD3IXX1VUj4PcM=
55-
github.com/bakito/go-log-logr-adapter v0.0.2/go.mod h1:B2tvB31L1Sxpkfhpj13QkJEisDNNKcC9FoYU8KL87AA=
5656
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
5757
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
5858
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -180,6 +180,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
180180
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
181181
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
182182
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
183+
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
184+
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
183185
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
184186
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
185187
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
@@ -305,8 +307,20 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
305307
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
306308
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
307309
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
310+
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
311+
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
312+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
313+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
314+
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
315+
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
316+
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
317+
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
308318
github.com/jhump/protoreflect v1.16.0 h1:54fZg+49widqXYQ0b+usAFHbMkBGR4PpXrsHc8+TBDg=
309319
github.com/jhump/protoreflect v1.16.0/go.mod h1:oYPd7nPvcBw/5wlDfm/AVmU9zH9BgqGCI469pGxfj/8=
320+
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
321+
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
322+
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
323+
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
310324
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
311325
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
312326
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
@@ -361,6 +375,8 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
361375
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
362376
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
363377
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
378+
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
379+
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
364380
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
365381
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
366382
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
@@ -917,6 +933,14 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
917933
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
918934
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
919935
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
936+
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
937+
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
938+
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
939+
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
940+
gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
941+
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
942+
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
943+
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
920944
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
921945
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
922946
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

pkg/config/store/config.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ var _ config.Config = &Config{}
2626
type Type = string
2727

2828
const (
29-
Memory Type = "memory"
29+
Memory Type = "memory"
30+
MySQL Type = "mysql"
31+
Postgres Type = "postgres"
3032
)
3133

3234
// Config defines the ResourceStore configuration

pkg/core/bootstrap/init.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,6 @@ import (
2828
_ "github.com/apache/dubbo-admin/pkg/discovery/mock"
2929
_ "github.com/apache/dubbo-admin/pkg/engine/kubernetes"
3030
_ "github.com/apache/dubbo-admin/pkg/store/memory"
31+
_ "github.com/apache/dubbo-admin/pkg/store/mysql"
32+
_ "github.com/apache/dubbo-admin/pkg/store/postgres"
3133
)
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package dbcommon
19+
20+
import (
21+
"database/sql"
22+
"fmt"
23+
"sync"
24+
"time"
25+
26+
"gorm.io/gorm"
27+
28+
storecfg "github.com/apache/dubbo-admin/pkg/config/store"
29+
"github.com/apache/dubbo-admin/pkg/core/logger"
30+
)
31+
32+
var (
33+
// pools stores all connection pools indexed by a unique key (storeType:address)
34+
pools = make(map[string]*ConnectionPool)
35+
// poolsMutex protects concurrent access to the pools map
36+
poolsMutex sync.RWMutex
37+
)
38+
39+
// ConnectionPoolConfig defines connection pool configuration
40+
type ConnectionPoolConfig struct {
41+
MaxIdleConns int // Maximum number of idle connections
42+
MaxOpenConns int // Maximum number of open connections
43+
ConnMaxLifetime time.Duration // Maximum lifetime of a connection
44+
ConnMaxIdleTime time.Duration // Maximum idle time of a connection
45+
}
46+
47+
// ConnectionPool manages database connections with connection pooling
48+
type ConnectionPool struct {
49+
db *gorm.DB
50+
sqlDB *sql.DB
51+
address string
52+
storeType storecfg.Type
53+
mu sync.RWMutex
54+
refCount int // Reference counter for the number of stores using this pool
55+
closeOnce sync.Once // Ensure Close is called only once
56+
closed bool // Track if the pool is closed
57+
}
58+
59+
// GetOrCreatePool returns or creates a connection pool for the given store type and address
60+
// It implements a singleton pattern with reference counting to allow pool reuse across multiple stores
61+
// If a pool already exists for the same storeType and address, it increments the reference count and returns the existing pool
62+
// Otherwise, it creates a new pool with the provided dialector
63+
func GetOrCreatePool(dialector gorm.Dialector, storeType storecfg.Type, address string, config *ConnectionPoolConfig) (*ConnectionPool, error) {
64+
if storeType == storecfg.Memory {
65+
return nil, fmt.Errorf("memory pool store is no need to create connection pool")
66+
}
67+
68+
poolKey := fmt.Sprintf("%s:%s", storeType, address)
69+
70+
poolsMutex.Lock()
71+
defer poolsMutex.Unlock()
72+
73+
// Check if pool already exists
74+
if existingPool, exists := pools[poolKey]; exists {
75+
// Increment reference count when reusing existing pool
76+
existingPool.refCount++
77+
logger.Infof("Reusing %s connection pool: address=%s, refCount=%d", storeType, address, existingPool.refCount)
78+
return existingPool, nil
79+
}
80+
81+
// Create new pool
82+
if config == nil {
83+
config = DefaultConnectionPoolConfig()
84+
}
85+
86+
pool, err := NewConnectionPool(dialector, storeType, address, config)
87+
if err != nil {
88+
return nil, err
89+
}
90+
91+
// Store the pool
92+
pools[poolKey] = pool
93+
logger.Infof("%s connection pool created successfully: address=%s, maxIdleConns=%d, maxOpenConns=%d",
94+
storeType, address, config.MaxIdleConns, config.MaxOpenConns)
95+
96+
return pool, nil
97+
}
98+
99+
// RemovePool removes a pool from the global registry
100+
// This should only be called when the pool's reference count reaches zero
101+
func RemovePool(storeType storecfg.Type, address string) {
102+
poolKey := fmt.Sprintf("%s:%s", storeType, address)
103+
104+
poolsMutex.Lock()
105+
defer poolsMutex.Unlock()
106+
107+
delete(pools, poolKey)
108+
logger.Infof("Removed %s connection pool from registry: address=%s", storeType, address)
109+
}
110+
111+
// DefaultConnectionPoolConfig returns default connection pool configuration
112+
func DefaultConnectionPoolConfig() *ConnectionPoolConfig {
113+
return &ConnectionPoolConfig{
114+
MaxIdleConns: 10, // Default: 10 idle connections
115+
MaxOpenConns: 100, // Default: 100 max open connections
116+
ConnMaxLifetime: time.Hour, // Default: 1 hour max lifetime
117+
ConnMaxIdleTime: 10 * time.Minute, // Default: 10 minutes max idle time
118+
}
119+
}
120+
121+
// NewConnectionPool creates a new connection pool
122+
func NewConnectionPool(dialector gorm.Dialector, storeType storecfg.Type, address string, config *ConnectionPoolConfig) (*ConnectionPool, error) {
123+
db, err := gorm.Open(dialector, &gorm.Config{})
124+
if err != nil {
125+
return nil, fmt.Errorf("failed to connect to %s: %w", storeType, err)
126+
}
127+
128+
sqlDB, err := db.DB()
129+
if err != nil {
130+
return nil, fmt.Errorf("failed to get underlying sql.DB: %w", err)
131+
}
132+
133+
// Configure connection pool
134+
sqlDB.SetMaxIdleConns(config.MaxIdleConns)
135+
sqlDB.SetMaxOpenConns(config.MaxOpenConns)
136+
sqlDB.SetConnMaxLifetime(config.ConnMaxLifetime)
137+
sqlDB.SetConnMaxIdleTime(config.ConnMaxIdleTime)
138+
139+
return &ConnectionPool{
140+
db: db,
141+
sqlDB: sqlDB,
142+
address: address,
143+
storeType: storeType,
144+
refCount: 1, // Initial reference count
145+
}, nil
146+
}
147+
148+
// GetDB returns the gorm.DB instance
149+
func (p *ConnectionPool) GetDB() *gorm.DB {
150+
p.mu.RLock()
151+
defer p.mu.RUnlock()
152+
return p.db
153+
}
154+
155+
// Address returns the connection address
156+
func (p *ConnectionPool) Address() string {
157+
p.mu.RLock()
158+
defer p.mu.RUnlock()
159+
return p.address
160+
}
161+
162+
// RefCount returns the current reference count
163+
func (p *ConnectionPool) RefCount() int {
164+
p.mu.RLock()
165+
defer p.mu.RUnlock()
166+
return p.refCount
167+
}
168+
169+
// IncrementRef increments the reference count
170+
func (p *ConnectionPool) IncrementRef() {
171+
p.mu.Lock()
172+
defer p.mu.Unlock()
173+
p.refCount++
174+
}
175+
176+
// Close closes the connection pool gracefully with reference counting
177+
// The pool is only actually closed when refCount reaches 0
178+
func (p *ConnectionPool) Close() error {
179+
p.mu.Lock()
180+
defer p.mu.Unlock()
181+
182+
if p.closed {
183+
return nil // Already closed
184+
}
185+
186+
p.refCount--
187+
logger.Infof("Decremented %s connection pool refCount: address=%s, refCount=%d", p.storeType, p.address, p.refCount)
188+
189+
// Only close the pool when no stores are using it
190+
if p.refCount <= 0 {
191+
var closeErr error
192+
p.closeOnce.Do(func() {
193+
if p.sqlDB != nil {
194+
logger.Infof("Closing %s connection pool: address=%s", p.storeType, p.address)
195+
closeErr = p.sqlDB.Close()
196+
p.closed = true
197+
}
198+
RemovePool(p.storeType, p.address)
199+
})
200+
return closeErr
201+
}
202+
203+
return nil
204+
}
205+
206+
// Ping checks if the database connection is alive
207+
func (p *ConnectionPool) Ping() error {
208+
p.mu.RLock()
209+
defer p.mu.RUnlock()
210+
211+
if p.sqlDB != nil {
212+
return p.sqlDB.Ping()
213+
}
214+
return fmt.Errorf("connection pool not initialized")
215+
}
216+
217+
// Stats returns database connection pool statistics
218+
func (p *ConnectionPool) Stats() sql.DBStats {
219+
p.mu.RLock()
220+
defer p.mu.RUnlock()
221+
222+
if p.sqlDB != nil {
223+
return p.sqlDB.Stats()
224+
}
225+
return sql.DBStats{}
226+
}

0 commit comments

Comments
 (0)