github.com/XiaoMi/Gaea@v1.2.5/backend/connection_pool.go (about) 1 // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package backend 16 17 import ( 18 "context" 19 "errors" 20 "sync" 21 "time" 22 23 "github.com/XiaoMi/Gaea/mysql" 24 "github.com/XiaoMi/Gaea/util" 25 ) 26 27 const ( 28 getConnTimeout = 2 * time.Second 29 PING_PEROID = 5 * time.Second 30 ) 31 32 var ( 33 // ErrConnectionPoolClosed means pool closed error 34 ErrConnectionPoolClosed = errors.New("connection pool is closed") 35 // DefaultCapacity default capacity of connection pool 36 DefaultCapacity = 64 37 ) 38 39 // connectionPoolImpl means connection pool with specific addr 40 type connectionPoolImpl struct { 41 mu sync.RWMutex 42 connections *util.ResourcePool 43 44 addr string 45 user string 46 password string 47 db string 48 49 charset string 50 collationID mysql.CollationID 51 52 capacity int // capacity of pool 53 maxCapacity int // max capacity of pool 54 idleTimeout time.Duration 55 } 56 57 // NewConnectionPool create connection pool 58 func NewConnectionPool(addr, user, password, db string, capacity, maxCapacity int, idleTimeout time.Duration, charset string, collationID mysql.CollationID) ConnectionPool { 59 cp := &connectionPoolImpl{addr: addr, user: user, password: password, db: db, capacity: capacity, maxCapacity: maxCapacity, idleTimeout: idleTimeout, charset: charset, collationID: collationID} 60 return cp 61 } 62 63 func (cp *connectionPoolImpl) pool() (p *util.ResourcePool) { 64 cp.mu.Lock() 65 p = cp.connections 66 cp.mu.Unlock() 67 return p 68 } 69 70 // Open open connection pool without error, should be called before use the pool 71 func (cp *connectionPoolImpl) Open() { 72 if cp.capacity == 0 { 73 cp.capacity = DefaultCapacity 74 } 75 76 if cp.maxCapacity == 0 { 77 cp.maxCapacity = cp.capacity 78 } 79 cp.mu.Lock() 80 defer cp.mu.Unlock() 81 cp.connections = util.NewResourcePool(cp.connect, cp.capacity, cp.maxCapacity, cp.idleTimeout) 82 return 83 } 84 85 // connect is used by the resource pool to create new resource.It's factory method 86 func (cp *connectionPoolImpl) connect() (util.Resource, error) { 87 c, err := NewDirectConnection(cp.addr, cp.user, cp.password, cp.db, cp.charset, cp.collationID) 88 if err != nil { 89 return nil, err 90 } 91 return &pooledConnectImpl{directConnection: c, pool: cp}, nil 92 } 93 94 // Addr return addr of connection pool 95 func (cp *connectionPoolImpl) Addr() string { 96 return cp.addr 97 } 98 99 // Close close connection pool 100 func (cp *connectionPoolImpl) Close() { 101 p := cp.pool() 102 if p == nil { 103 return 104 } 105 p.Close() 106 cp.mu.Lock() 107 cp.connections = nil 108 cp.mu.Unlock() 109 return 110 } 111 112 // tryReuse reset params of connection before reuse 113 func (cp *connectionPoolImpl) tryReuse(pc *pooledConnectImpl) error { 114 return pc.directConnection.ResetConnection() 115 } 116 117 // Get return a connection, you should call PooledConnect's Recycle once done 118 func (cp *connectionPoolImpl) Get(ctx context.Context) (pc PooledConnect, err error) { 119 p := cp.pool() 120 if p == nil { 121 return nil, ErrConnectionPoolClosed 122 } 123 124 getCtx, cancel := context.WithTimeout(ctx, getConnTimeout) 125 defer cancel() 126 r, err := p.Get(getCtx) 127 if err != nil { 128 return nil, err 129 } 130 131 pc = r.(*pooledConnectImpl) 132 133 //do ping when over the ping time. if error happen, create new one 134 if !pc.GetReturnTime().IsZero() && time.Until(pc.GetReturnTime().Add(PING_PEROID)) < 0 { 135 if err = pc.Ping(); err != nil { 136 err = pc.Reconnect() 137 } 138 } 139 return pc, err 140 } 141 142 // Put recycle a connection into the pool 143 func (cp *connectionPoolImpl) Put(pc PooledConnect) { 144 p := cp.pool() 145 if p == nil { 146 panic(ErrConnectionPoolClosed) 147 } 148 149 if pc == nil { 150 p.Put(nil) 151 } else if err := cp.tryReuse(pc.(*pooledConnectImpl)); err != nil { 152 pc.Close() 153 p.Put(nil) 154 } else { 155 p.Put(pc) 156 } 157 } 158 159 // SetCapacity alert the size of the pool at runtime 160 func (cp *connectionPoolImpl) SetCapacity(capacity int) (err error) { 161 cp.mu.Lock() 162 defer cp.mu.Unlock() 163 if cp.connections != nil { 164 err = cp.connections.SetCapacity(capacity) 165 if err != nil { 166 return err 167 } 168 } 169 cp.capacity = capacity 170 return nil 171 } 172 173 // SetIdleTimeout set the idleTimeout of the pool 174 func (cp *connectionPoolImpl) SetIdleTimeout(idleTimeout time.Duration) { 175 cp.mu.Lock() 176 defer cp.mu.Unlock() 177 if cp.connections != nil { 178 cp.connections.SetIdleTimeout(idleTimeout) 179 } 180 cp.idleTimeout = idleTimeout 181 } 182 183 // StatsJSON return the pool stats as JSON object. 184 func (cp *connectionPoolImpl) StatsJSON() string { 185 p := cp.pool() 186 if p == nil { 187 return "{}" 188 } 189 return p.StatsJSON() 190 } 191 192 // Capacity return the pool capacity 193 func (cp *connectionPoolImpl) Capacity() int64 { 194 p := cp.pool() 195 if p == nil { 196 return 0 197 } 198 return p.Capacity() 199 } 200 201 // Available returns the number of available connections in the pool 202 func (cp *connectionPoolImpl) Available() int64 { 203 p := cp.pool() 204 if p == nil { 205 return 0 206 } 207 return p.Available() 208 } 209 210 // Active returns the number of active connections in the pool 211 func (cp *connectionPoolImpl) Active() int64 { 212 p := cp.pool() 213 if p == nil { 214 return 0 215 } 216 return p.Active() 217 } 218 219 // InUse returns the number of in-use connections in the pool 220 func (cp *connectionPoolImpl) InUse() int64 { 221 p := cp.pool() 222 if p == nil { 223 return 0 224 } 225 return p.InUse() 226 } 227 228 // MaxCap returns the maximum size of the pool 229 func (cp *connectionPoolImpl) MaxCap() int64 { 230 p := cp.pool() 231 if p == nil { 232 return 0 233 } 234 return p.MaxCap() 235 } 236 237 // WaitCount returns how many clients are waitting for a connection 238 func (cp *connectionPoolImpl) WaitCount() int64 { 239 p := cp.pool() 240 if p == nil { 241 return 0 242 } 243 return p.WaitCount() 244 } 245 246 // WaitTime returns the time wait for a connection 247 func (cp *connectionPoolImpl) WaitTime() time.Duration { 248 p := cp.pool() 249 if p == nil { 250 return 0 251 } 252 return p.WaitTime() 253 } 254 255 // IdleTimeout returns the idle timeout for the pool 256 func (cp *connectionPoolImpl) IdleTimeout() time.Duration { 257 p := cp.pool() 258 if p == nil { 259 return 0 260 } 261 return p.IdleTimeout() 262 } 263 264 // IdleClosed return the number of closed connections for the pool 265 func (cp *connectionPoolImpl) IdleClosed() int64 { 266 p := cp.pool() 267 if p == nil { 268 return 0 269 } 270 return p.IdleClosed() 271 }