pool.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. // Copyright 2012 Gary Burd
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License"): you may
  4. // not use this file except in compliance with the License. You may obtain
  5. // 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, WITHOUT
  11. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. // License for the specific language governing permissions and limitations
  13. // under the License.
  14. package redis
  15. import (
  16. "container/list"
  17. "errors"
  18. "sync"
  19. "time"
  20. )
  21. var nowFunc = time.Now // for testing
  22. var errPoolClosed = errors.New("redigo: connection pool closed")
  23. // Pool maintains a pool of connections. The application calls the Get method
  24. // to get a connection from the pool and the connection's Close method to
  25. // return the connection's resources to the pool.
  26. //
  27. // The following example shows how to use a pool in a web application. The
  28. // application creates a pool at application startup and makes it available to
  29. // request handlers, possibly using a global variable:
  30. //
  31. // var server string // host:port of server
  32. // var password string
  33. // ...
  34. //
  35. // pool = &redis.Pool{
  36. // MaxIdle: 3,
  37. // IdleTimeout: 240 * time.Second,
  38. // Dial: func () (redis.Conn, error) {
  39. // c, err := redis.Dial("tcp", server)
  40. // if err != nil {
  41. // return nil, err
  42. // }
  43. // if err := c.Do("AUTH", password); err != nil {
  44. // c.Close()
  45. // return nil, err
  46. // }
  47. // return c, err
  48. // },
  49. // }
  50. //
  51. // This pool has a maximum of three connections to the server specified by the
  52. // variable "server". Each connection is authenticated using a password.
  53. //
  54. // A request handler gets a connection from the pool and closes the connection
  55. // when the handler is done:
  56. //
  57. // conn, err := pool.Get()
  58. // defer conn.Close()
  59. // // do something with the connection
  60. type Pool struct {
  61. // Dial is an application supplied function for creating new connections.
  62. Dial func() (Conn, error)
  63. // Maximum number of idle connections in the pool.
  64. MaxIdle int
  65. // Close connections after remaining idle for this duration. If the value
  66. // is zero, then idle connections are not closed. Applications should set
  67. // the timeout to a value less than the server's timeout.
  68. IdleTimeout time.Duration
  69. // mu protects fields defined below.
  70. mu sync.Mutex
  71. closed bool
  72. // Stack of idleConn with most recently used at the front.
  73. idle list.List
  74. }
  75. type idleConn struct {
  76. c Conn
  77. t time.Time
  78. }
  79. // NewPool returns a pool that uses newPool to create connections as needed.
  80. // The pool keeps a maximum of maxIdle idle connections.
  81. func NewPool(newFn func() (Conn, error), maxIdle int) *Pool {
  82. return &Pool{Dial: newFn, MaxIdle: maxIdle}
  83. }
  84. // Get gets a connection from the pool.
  85. func (p *Pool) Get() Conn {
  86. return &pooledConnection{p: p}
  87. }
  88. // Close releases the resources used by the pool.
  89. func (p *Pool) Close() error {
  90. p.mu.Lock()
  91. idle := p.idle
  92. p.idle.Init()
  93. p.closed = true
  94. p.mu.Unlock()
  95. for e := idle.Front(); e != nil; e = e.Next() {
  96. e.Value.(idleConn).c.Close()
  97. }
  98. return nil
  99. }
  100. func (p *Pool) get() (c Conn, err error) {
  101. p.mu.Lock()
  102. if p.IdleTimeout > 0 {
  103. for {
  104. e := p.idle.Back()
  105. if e == nil || nowFunc().Before(e.Value.(idleConn).t) {
  106. break
  107. }
  108. cStale := e.Value.(idleConn).c
  109. p.idle.Remove(e)
  110. // Release the pool lock during the potentially long call to the
  111. // connection's Close method.
  112. p.mu.Unlock()
  113. cStale.Close()
  114. p.mu.Lock()
  115. }
  116. }
  117. if p.closed {
  118. p.mu.Unlock()
  119. return nil, errors.New("redigo: get on closed pool")
  120. }
  121. if e := p.idle.Front(); e != nil {
  122. c = e.Value.(idleConn).c
  123. p.idle.Remove(e)
  124. }
  125. p.mu.Unlock()
  126. if c == nil {
  127. c, err = p.Dial()
  128. }
  129. return c, err
  130. }
  131. func (p *Pool) put(c Conn) error {
  132. p.mu.Lock()
  133. if !p.closed {
  134. p.idle.PushFront(idleConn{t: nowFunc().Add(p.IdleTimeout), c: c})
  135. if p.idle.Len() > p.MaxIdle {
  136. c = p.idle.Remove(p.idle.Back()).(idleConn).c
  137. } else {
  138. c = nil
  139. }
  140. }
  141. p.mu.Unlock()
  142. if c != nil {
  143. return c.Close()
  144. }
  145. return nil
  146. }
  147. type pooledConnection struct {
  148. c Conn
  149. err error
  150. p *Pool
  151. }
  152. func (c *pooledConnection) get() error {
  153. if c.err == nil && c.c == nil {
  154. c.c, c.err = c.p.get()
  155. }
  156. return c.err
  157. }
  158. func (c *pooledConnection) Close() (err error) {
  159. if c.c != nil {
  160. c.c.Do("")
  161. if c.c.Err() != nil {
  162. err = c.c.Close()
  163. } else {
  164. err = c.p.put(c.c)
  165. }
  166. c.c = nil
  167. c.err = errPoolClosed
  168. }
  169. return err
  170. }
  171. func (c *pooledConnection) Err() error {
  172. if err := c.get(); err != nil {
  173. return err
  174. }
  175. return c.c.Err()
  176. }
  177. func (c *pooledConnection) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
  178. if err := c.get(); err != nil {
  179. return nil, err
  180. }
  181. return c.c.Do(commandName, args...)
  182. }
  183. func (c *pooledConnection) Send(commandName string, args ...interface{}) error {
  184. if err := c.get(); err != nil {
  185. return err
  186. }
  187. return c.c.Send(commandName, args...)
  188. }
  189. func (c *pooledConnection) Flush() error {
  190. if err := c.get(); err != nil {
  191. return err
  192. }
  193. return c.c.Flush()
  194. }
  195. func (c *pooledConnection) Receive() (reply interface{}, err error) {
  196. if err := c.get(); err != nil {
  197. return nil, err
  198. }
  199. return c.c.Receive()
  200. }