@@ -56,6 +56,8 @@ type mongoServer struct {
5656 closed bool
5757 abended bool
5858 poolWaiter * sync.Cond
59+ minPoolSize int
60+ maxIdleTimeMS int
5961}
6062
6163type dialer struct {
@@ -77,18 +79,23 @@ type mongoServerInfo struct {
7779
7880var defaultServerInfo mongoServerInfo
7981
80- func newServer (addr string , tcpaddr * net.TCPAddr , syncChan chan bool , dial dialer ) * mongoServer {
82+ func newServer (addr string , tcpaddr * net.TCPAddr , syncChan chan bool , dial dialer , minPoolSize , maxIdleTimeMS int ) * mongoServer {
8183 server := & mongoServer {
82- Addr : addr ,
83- ResolvedAddr : tcpaddr .String (),
84- tcpaddr : tcpaddr ,
85- sync : syncChan ,
86- dial : dial ,
87- info : & defaultServerInfo ,
88- pingValue : time .Hour , // Push it back before an actual ping.
84+ Addr : addr ,
85+ ResolvedAddr : tcpaddr .String (),
86+ tcpaddr : tcpaddr ,
87+ sync : syncChan ,
88+ dial : dial ,
89+ info : & defaultServerInfo ,
90+ pingValue : time .Hour , // Push it back before an actual ping.
91+ minPoolSize : minPoolSize ,
92+ maxIdleTimeMS : maxIdleTimeMS ,
8993 }
9094 server .poolWaiter = sync .NewCond (server )
9195 go server .pinger (true )
96+ if maxIdleTimeMS != 0 {
97+ go server .poolShrinker ()
98+ }
9299 return server
93100}
94101
@@ -277,6 +284,7 @@ func (server *mongoServer) close(waitForIdle bool) {
277284func (server * mongoServer ) RecycleSocket (socket * mongoSocket ) {
278285 server .Lock ()
279286 if ! server .closed {
287+ socket .lastTimeUsed = time .Now ()
280288 server .unusedSockets = append (server .unusedSockets , socket )
281289 }
282290 // If anybody is waiting for a connection, they should try now.
@@ -410,6 +418,53 @@ func (server *mongoServer) pinger(loop bool) {
410418 }
411419}
412420
421+ func (server * mongoServer ) poolShrinker () {
422+ ticker := time .NewTicker (1 * time .Minute )
423+ for _ = range ticker .C {
424+ if server .closed {
425+ ticker .Stop ()
426+ return
427+ }
428+ server .Lock ()
429+ unused := len (server .unusedSockets )
430+ if unused < server .minPoolSize {
431+ server .Unlock ()
432+ continue
433+ }
434+ now := time .Now ()
435+ end := 0
436+ reclaimMap := map [* mongoSocket ]struct {}{}
437+ // Because the acquisition and recycle are done at the tail of array,
438+ // the head is always the oldest unused socket.
439+ for _ , s := range server .unusedSockets [:unused - server .minPoolSize ] {
440+ if s .lastTimeUsed .Add (time .Duration (server .maxIdleTimeMS ) * time .Millisecond ).After (now ) {
441+ break
442+ }
443+ end ++
444+ reclaimMap [s ] = struct {}{}
445+ }
446+ tbr := server .unusedSockets [:end ]
447+ if end > 0 {
448+ next := make ([]* mongoSocket , unused - end )
449+ copy (next , server .unusedSockets [end :])
450+ server .unusedSockets = next
451+ remainSockets := []* mongoSocket {}
452+ for _ , s := range server .liveSockets {
453+ if _ , ok := reclaimMap [s ]; ! ok {
454+ remainSockets = append (remainSockets , s )
455+ }
456+ }
457+ server .liveSockets = remainSockets
458+ stats .conn (- 1 * end , server .info .Master )
459+ }
460+ server .Unlock ()
461+
462+ for _ , s := range tbr {
463+ s .Close ()
464+ }
465+ }
466+ }
467+
413468type mongoServerSlice []* mongoServer
414469
415470func (s mongoServerSlice ) Len () int {
0 commit comments