@@ -28,13 +28,11 @@ type ConnManager struct {
28
28
// Outside of tests it is a real clock.
29
29
clock clockwork.Clock
30
30
31
- // Number of connections desirable to maintain.
32
31
targetConnCount uint
33
32
34
33
// Number of current connections, collectively in all pools or acquired.
35
34
curConnCount atomic.Int64
36
35
37
- // ConnCreator is used to create new connections.
38
36
connCreator ConnCreator
39
37
40
38
// Connection pools. curConnCount connections are either in one
@@ -43,13 +41,7 @@ type ConnManager struct {
43
41
idleConns chan * ManagedConn // Ready to be acquired.
44
42
recreateConns chan * ManagedConn // Pending to be recreated.
45
43
46
- // Approximate period to wait before flushing connections after
47
- // the connection is released by the user. Setting this value to >0 avoids
48
- // unnecessary flushes when the connection is acquired and released frequently.
49
- flushPeriod time.Duration
50
-
51
- // Period to reconnect connections. Each connection is periodically
52
- // reconnected approximately every reconnectPeriod.
44
+ flushPeriod time.Duration
53
45
reconnectPeriod time.Duration
54
46
55
47
// Flags to indicate if the goroutines are stopped.
@@ -104,28 +96,59 @@ type Conn interface {
104
96
// Flush the connection. This is typically to send any buffered data.
105
97
// Will be called periodically (see ConnManager flushPeriod) and
106
98
// before ConnManager.Stop returns.
107
- Flush () error
99
+ Flush (context.Context ) error
100
+ }
101
+
102
+ type ConnManagerSettings struct {
103
+ Logger * zap.Logger
104
+
105
+ // Creator helps create new connections.
106
+ Creator ConnCreator
107
+
108
+ // TargetConnCount is the number of connections desirable to maintain.
109
+ // Must be >0.
110
+ TargetConnCount uint
111
+
112
+ // FlushPeriod is the approximate period to wait before flushing connections after
113
+ // the user releases the connection. Setting this value to >0 avoids
114
+ // unnecessary flushes when the connection is acquired and released frequently.
115
+ // Setting this to 0 ensures flushing is done after every connection release.
116
+ FlushPeriod time.Duration
117
+
118
+ // ReconnectPeriod is the interval to reconnect connections. Each connection is
119
+ // periodically reconnected approximately every reconnectPeriod.
120
+ ReconnectPeriod time.Duration
108
121
}
109
122
110
- func NewConnManager (
111
- logger * zap.Logger ,
112
- creator ConnCreator ,
113
- targetConnCount uint ,
114
- flushPeriod time.Duration ,
115
- reconnectPeriod time.Duration ,
116
- ) * ConnManager {
123
+ func NewConnManager (set ConnManagerSettings ) (* ConnManager , error ) {
124
+ if set .Logger == nil {
125
+ set .Logger = zap .NewNop ()
126
+ }
127
+
128
+ if set .TargetConnCount == 0 {
129
+ return nil , errors .New ("TargetConnCount must be >0" )
130
+ }
131
+
132
+ if set .FlushPeriod <= 0 {
133
+ return nil , errors .New ("FlushPeriod must be >=0" )
134
+ }
135
+
136
+ if set .ReconnectPeriod <= 0 {
137
+ return nil , errors .New ("ReconnectPeriod must be >0" )
138
+ }
139
+
117
140
return & ConnManager {
118
- logger : logger ,
141
+ logger : set . Logger ,
119
142
clock : clockwork .NewRealClock (),
120
- connCreator : creator ,
121
- targetConnCount : targetConnCount ,
122
- idleConns : make (chan * ManagedConn , targetConnCount ),
123
- recreateConns : make (chan * ManagedConn , targetConnCount ),
124
- flushPeriod : flushPeriod ,
125
- reconnectPeriod : reconnectPeriod ,
143
+ connCreator : set . Creator ,
144
+ targetConnCount : set . TargetConnCount ,
145
+ idleConns : make (chan * ManagedConn , set . TargetConnCount ),
146
+ recreateConns : make (chan * ManagedConn , set . TargetConnCount ),
147
+ flushPeriod : set . FlushPeriod ,
148
+ reconnectPeriod : set . ReconnectPeriod ,
126
149
stoppedCond : NewCancellableCond (),
127
150
stopSignal : make (chan struct {}),
128
- }
151
+ }, nil
129
152
}
130
153
131
154
// Start starts the connection manager. It will immediately start
@@ -194,7 +217,7 @@ func (c *ConnManager) closeAll(ctx context.Context) error {
194
217
if conn .conn != nil {
195
218
// Flush if needs a flush and is not discarded.
196
219
if ! discarded && conn .needsFlush {
197
- if err := conn .conn .Flush (); err != nil {
220
+ if err := conn .conn .Flush (ctx ); err != nil {
198
221
c .logger .Debug ("Failed to flush connection" , zap .Error (err ))
199
222
errs = append (errs , err )
200
223
continue
@@ -240,14 +263,14 @@ func (c *ConnManager) Acquire(ctx context.Context) (*ManagedConn, error) {
240
263
// If the connection was last flushed more than flushPeriod ago, it will
241
264
// be flushed otherwise it will be marked as needing to be flushed at the
242
265
// next opportunity.
243
- func (c * ConnManager ) Release (conn * ManagedConn ) {
266
+ func (c * ConnManager ) Release (ctx context. Context , conn * ManagedConn ) {
244
267
if ! conn .isAcquired {
245
268
panic ("connection is not acquired" )
246
269
}
247
270
conn .isAcquired = false
248
- if c .clock .Since (conn .lastFlush ) >= c .flushPeriod {
271
+ if c .clock .Since (conn .lastFlush ) >= c .flushPeriod || c . flushPeriod == 0 {
249
272
// Time to flush the connection.
250
- if err := conn .conn .Flush (); err != nil {
273
+ if err := conn .conn .Flush (ctx ); err != nil {
251
274
c .logger .Error ("Failed to flush connection. Closing connection." , zap .Error (err ))
252
275
// Something went wrong, we need to recreate the connection since it
253
276
// may no longer be usable.
@@ -282,11 +305,23 @@ func (c *ConnManager) flusher() {
282
305
c .stoppedCond .Cond .Broadcast ()
283
306
}()
284
307
308
+ if c .flushPeriod == 0 {
309
+ // flusher is not needed, we will always flush immediately in Release().
310
+ return
311
+ }
312
+
285
313
ticker := c .clock .NewTicker (c .flushPeriod )
286
314
315
+ // Context that cancels on stopSignal.
316
+ ctx , cancel := context .WithCancel (context .Background ())
317
+ go func () {
318
+ defer cancel ()
319
+ <- c .stopSignal
320
+ }()
321
+
287
322
for {
288
323
select {
289
- case <- c . stopSignal :
324
+ case <- ctx . Done () :
290
325
return
291
326
case <- ticker .Chan ():
292
327
loop:
@@ -297,7 +332,7 @@ func (c *ConnManager) flusher() {
297
332
case conn := <- c .idleConns :
298
333
if conn .needsFlush {
299
334
conn .needsFlush = false
300
- if err := conn .conn .Flush (); err != nil {
335
+ if err := conn .conn .Flush (ctx ); err != nil {
301
336
c .logger .Error ("Failed to flush connection. Closing connection." , zap .Error (err ))
302
337
// Something went wrong, we need to recreate the connection since it
303
338
// may no longer be usable.
@@ -329,19 +364,26 @@ func (c *ConnManager) durationLimiter() {
329
364
c .stoppedCond .Cond .Broadcast ()
330
365
}()
331
366
367
+ // Context that cancels on stopSignal.
368
+ ctx , cancel := context .WithCancel (context .Background ())
369
+ go func () {
370
+ defer cancel ()
371
+ <- c .stopSignal
372
+ }()
373
+
332
374
// Each connection will be reconnected at approximately reconnectPeriod interval.
333
375
// We reconnect one per tick.
334
376
ticker := c .clock .NewTicker (c .reconnectPeriod / time .Duration (c .targetConnCount ))
335
377
defer ticker .Stop ()
336
378
for {
337
379
select {
338
- case <- c . stopSignal :
380
+ case <- ctx . Done () :
339
381
return
340
382
case <- ticker .Chan ():
341
383
// Find an idle connection
342
384
var conn * ManagedConn
343
385
select {
344
- case <- c . stopSignal :
386
+ case <- ctx . Done () :
345
387
return
346
388
case conn = <- c .idleConns :
347
389
}
@@ -351,7 +393,7 @@ func (c *ConnManager) durationLimiter() {
351
393
if conn .needsFlush {
352
394
conn .needsFlush = false
353
395
// Flush it first.
354
- if err := conn .conn .Flush (); err != nil {
396
+ if err := conn .conn .Flush (ctx ); err != nil {
355
397
c .logger .Error ("Failed to flush connection. Closing connection." , zap .Error (err ))
356
398
}
357
399
}
0 commit comments