@@ -2,6 +2,7 @@ package client
2
2
3
3
import (
4
4
"context"
5
+ "log"
5
6
"math"
6
7
"math/rand"
7
8
"sync"
@@ -77,43 +78,46 @@ var (
77
78
MaxNewConnectionAtOnce = 5
78
79
)
79
80
80
- // NewPool initializes new connection pool and uses params: addr, user, password, dbName and options.
81
- // minAlive specifies the minimum number of open connections that the pool will try to maintain.
82
- // maxAlive specifies the maximum number of open connections (for internal reasons,
83
- // may be greater by 1 inside newConnectionProducer).
84
- // maxIdle specifies the maximum number of idle connections (see DefaultIdleTimeout).
85
- func NewPool (
86
- logFunc LogFunc ,
87
- minAlive int ,
88
- maxAlive int ,
89
- maxIdle int ,
81
+ // NewPoolWithOptions initializes new connection pool and uses params: addr, user, password, dbName and options.
82
+ func NewPoolWithOptions (
90
83
addr string ,
91
84
user string ,
92
85
password string ,
93
86
dbName string ,
94
- options ... func (conn * Conn ),
95
- ) * Pool {
96
- if minAlive > maxAlive {
97
- minAlive = maxAlive
87
+ options ... PoolOption ,
88
+ ) (* Pool , error ) {
89
+ po := getDefaultPoolOptions ()
90
+
91
+ po .addr = addr
92
+ po .user = user
93
+ po .password = password
94
+ po .dbName = dbName
95
+
96
+ for _ , o := range options {
97
+ o (& po )
98
+ }
99
+
100
+ if po .minAlive > po .maxAlive {
101
+ po .minAlive = po .maxAlive
98
102
}
99
- if maxIdle > maxAlive {
100
- maxIdle = maxAlive
103
+ if po . maxIdle > po . maxAlive {
104
+ po . maxIdle = po . maxAlive
101
105
}
102
- if maxIdle <= minAlive {
103
- maxIdle = minAlive
106
+ if po . maxIdle <= po . minAlive {
107
+ po . maxIdle = po . minAlive
104
108
}
105
109
106
110
pool := & Pool {
107
- logFunc : logFunc ,
108
- minAlive : minAlive ,
109
- maxAlive : maxAlive ,
110
- maxIdle : maxIdle ,
111
+ logFunc : po . logFunc ,
112
+ minAlive : po . minAlive ,
113
+ maxAlive : po . maxAlive ,
114
+ maxIdle : po . maxIdle ,
111
115
112
116
idleCloseTimeout : Timestamp (math .Ceil (DefaultIdleTimeout .Seconds ())),
113
117
idlePingTimeout : Timestamp (math .Ceil (MaxIdleTimeoutWithoutPing .Seconds ())),
114
118
115
119
connect : func () (* Conn , error ) {
116
- return Connect (addr , user , password , dbName , options ... )
120
+ return Connect (addr , user , password , dbName , po . connOptions ... )
117
121
},
118
122
119
123
readyConnection : make (chan Connection ),
@@ -127,13 +131,56 @@ func NewPool(
127
131
go pool .newConnectionProducer ()
128
132
129
133
if pool .minAlive > 0 {
130
- pool .logFunc (`Pool: Setup %d new connections (minimal pool size)...` , pool .minAlive )
131
- pool .startNewConnections (pool .minAlive )
134
+ go pool .startNewConnections (pool .minAlive )
132
135
}
133
136
134
137
pool .wg .Add (1 )
135
138
go pool .closeOldIdleConnections ()
136
139
140
+ if po .newPoolPingTimeout > 0 {
141
+ ctx , cancel := context .WithTimeout (pool .ctx , po .newPoolPingTimeout )
142
+ err := pool .checkConnection (ctx )
143
+ cancel ()
144
+ if err != nil {
145
+ pool .Close ()
146
+ return nil , errors .Errorf ("checkConnection: %s" , err )
147
+ }
148
+ }
149
+
150
+ return pool , nil
151
+ }
152
+
153
+ // NewPool initializes new connection pool and uses params: addr, user, password, dbName and options.
154
+ // minAlive specifies the minimum number of open connections that the pool will try to maintain.
155
+ // maxAlive specifies the maximum number of open connections (for internal reasons,
156
+ // may be greater by 1 inside newConnectionProducer).
157
+ // maxIdle specifies the maximum number of idle connections (see DefaultIdleTimeout).
158
+ //
159
+ // Deprecated: use NewPoolWithOptions
160
+ func NewPool (
161
+ logFunc LogFunc ,
162
+ minAlive int ,
163
+ maxAlive int ,
164
+ maxIdle int ,
165
+ addr string ,
166
+ user string ,
167
+ password string ,
168
+ dbName string ,
169
+ options ... func (conn * Conn ),
170
+ ) * Pool {
171
+ pool , err := NewPoolWithOptions (
172
+ addr ,
173
+ user ,
174
+ password ,
175
+ dbName ,
176
+ WithLogFunc (logFunc ),
177
+ WithPoolLimits (minAlive , maxAlive , maxIdle ),
178
+ WithConnOptions (options ... ),
179
+ )
180
+ if err != nil {
181
+ pool .logFunc (`Pool: NewPool: %s` , err .Error ())
182
+ }
183
+
137
184
return pool
138
185
}
139
186
@@ -235,6 +282,7 @@ func (pool *Pool) putConnectionUnsafe(connection Connection) {
235
282
236
283
func (pool * Pool ) newConnectionProducer () {
237
284
defer pool .wg .Done ()
285
+
238
286
var connection Connection
239
287
var err error
240
288
@@ -263,10 +311,24 @@ func (pool *Pool) newConnectionProducer() {
263
311
pool .synchro .stats .TotalCount -- // Bad luck, should try again
264
312
pool .synchro .Unlock ()
265
313
266
- time .Sleep (time .Duration (10 + rand .Intn (90 )) * time .Millisecond )
267
- continue
314
+ pool .logFunc ("Cannot establish new db connection: %s" , err .Error ())
315
+
316
+ timer := time .NewTimer (
317
+ time .Duration (10 + rand .Intn (90 )) * time .Millisecond ,
318
+ )
319
+
320
+ select {
321
+ case <- timer .C :
322
+ continue
323
+ case <- pool .ctx .Done ():
324
+ if ! timer .Stop () {
325
+ <- timer .C
326
+ }
327
+ return
328
+ }
268
329
}
269
330
}
331
+
270
332
select {
271
333
case pool .readyConnection <- connection :
272
334
case <- pool .ctx .Done ():
@@ -309,6 +371,7 @@ func (pool *Pool) getIdleConnectionUnsafe() Connection {
309
371
310
372
func (pool * Pool ) closeOldIdleConnections () {
311
373
defer pool .wg .Done ()
374
+
312
375
var toPing []Connection
313
376
314
377
ticker := time .NewTicker (5 * time .Second )
@@ -468,13 +531,17 @@ func (pool *Pool) closeConn(conn *Conn) {
468
531
}
469
532
470
533
func (pool * Pool ) startNewConnections (count int ) {
534
+ pool .logFunc (`Pool: Setup %d new connections (minimal pool size)...` , count )
535
+
471
536
connections := make ([]Connection , 0 , count )
472
537
for i := 0 ; i < count ; i ++ {
473
538
if conn , err := pool .createNewConnection (); err == nil {
474
539
pool .synchro .Lock ()
475
540
pool .synchro .stats .TotalCount ++
476
541
pool .synchro .Unlock ()
477
542
connections = append (connections , conn )
543
+ } else {
544
+ pool .logFunc (`Pool: createNewConnection: %s` , err )
478
545
}
479
546
}
480
547
@@ -512,3 +579,34 @@ func (pool *Pool) Close() {
512
579
pool .synchro .idleConnections = nil
513
580
pool .synchro .Unlock ()
514
581
}
582
+
583
+ // checkConnection tries to connect and ping DB server
584
+ func (pool * Pool ) checkConnection (ctx context.Context ) error {
585
+ errChan := make (chan error , 1 )
586
+
587
+ go func () {
588
+ conn , err := pool .connect ()
589
+ if err == nil {
590
+ err = conn .Ping ()
591
+ _ = conn .Close ()
592
+ }
593
+ errChan <- err
594
+ }()
595
+
596
+ select {
597
+ case err := <- errChan :
598
+ return err
599
+ case <- ctx .Done ():
600
+ return ctx .Err ()
601
+ }
602
+ }
603
+
604
+ // getDefaultPoolOptions returns pool config for low load services
605
+ func getDefaultPoolOptions () poolOptions {
606
+ return poolOptions {
607
+ logFunc : log .Printf ,
608
+ minAlive : 1 ,
609
+ maxAlive : 10 ,
610
+ maxIdle : 2 ,
611
+ }
612
+ }
0 commit comments