gopkg.in/rethinkdb/rethinkdb-go.v6@v6.2.2/pool.go (about)

     1  package rethinkdb
     2  
     3  import (
     4  	"errors"
     5  	"sync"
     6  	"sync/atomic"
     7  
     8  	"golang.org/x/net/context"
     9  )
    10  
    11  var (
    12  	errPoolClosed = errors.New("rethinkdb: pool is closed")
    13  )
    14  
    15  const (
    16  	poolIsNotClosed int32 = 0
    17  	poolIsClosed    int32 = 1
    18  )
    19  
    20  type connFactory func(host string, opts *ConnectOpts) (*Connection, error)
    21  
    22  // A Pool is used to store a pool of connections to a single RethinkDB server
    23  type Pool struct {
    24  	host Host
    25  	opts *ConnectOpts
    26  
    27  	conns   []*Connection
    28  	pointer int32
    29  	closed  int32
    30  
    31  	connFactory connFactory
    32  
    33  	mu sync.Mutex // protects lazy creating connections
    34  }
    35  
    36  // NewPool creates a new connection pool for the given host
    37  func NewPool(host Host, opts *ConnectOpts) (*Pool, error) {
    38  	return newPool(host, opts, NewConnection)
    39  }
    40  
    41  func newPool(host Host, opts *ConnectOpts, connFactory connFactory) (*Pool, error) {
    42  	initialCap := opts.InitialCap
    43  	if initialCap <= 0 {
    44  		// Fallback to MaxIdle if InitialCap is zero, this should be removed
    45  		// when MaxIdle is removed
    46  		initialCap = opts.MaxIdle
    47  	}
    48  
    49  	maxOpen := opts.MaxOpen
    50  	if maxOpen <= 0 {
    51  		maxOpen = 1
    52  	}
    53  
    54  	conns := make([]*Connection, maxOpen)
    55  	var err error
    56  	for i := 0; i < opts.InitialCap; i++ {
    57  		conns[i], err = connFactory(host.String(), opts)
    58  		if err != nil {
    59  			return nil, err
    60  		}
    61  	}
    62  
    63  	return &Pool{
    64  		conns:       conns,
    65  		pointer:     -1,
    66  		host:        host,
    67  		opts:        opts,
    68  		connFactory: connFactory,
    69  		closed:      poolIsNotClosed,
    70  	}, nil
    71  }
    72  
    73  // Ping verifies a connection to the database is still alive,
    74  // establishing a connection if necessary.
    75  func (p *Pool) Ping() error {
    76  	_, err := p.conn()
    77  	return err
    78  }
    79  
    80  // Close closes the database, releasing any open resources.
    81  //
    82  // It is rare to Close a Pool, as the Pool handle is meant to be
    83  // long-lived and shared between many goroutines.
    84  func (p *Pool) Close() error {
    85  	if atomic.LoadInt32(&p.closed) == poolIsClosed {
    86  		return nil
    87  	}
    88  
    89  	p.mu.Lock()
    90  	defer p.mu.Unlock()
    91  
    92  	if p.closed == poolIsClosed {
    93  		return nil
    94  	}
    95  	p.closed = poolIsClosed
    96  
    97  	for _, c := range p.conns {
    98  		if c != nil {
    99  			err := c.Close()
   100  			if err != nil {
   101  				return err
   102  			}
   103  		}
   104  	}
   105  
   106  	return nil
   107  }
   108  
   109  func (p *Pool) conn() (*Connection, error) {
   110  	if atomic.LoadInt32(&p.closed) == poolIsClosed {
   111  		return nil, errPoolClosed
   112  	}
   113  
   114  	pos := atomic.AddInt32(&p.pointer, 1)
   115  	if pos == int32(len(p.conns)) {
   116  		atomic.StoreInt32(&p.pointer, 0)
   117  	}
   118  	pos = pos % int32(len(p.conns))
   119  
   120  	var err error
   121  
   122  	if p.conns[pos] == nil {
   123  		p.mu.Lock()
   124  		defer p.mu.Unlock()
   125  
   126  		if p.conns[pos] == nil {
   127  			p.conns[pos], err = p.connFactory(p.host.String(), p.opts)
   128  			if err != nil {
   129  				return nil, err
   130  			}
   131  		}
   132  	} else if p.conns[pos].isBad() {
   133  		// connBad connection needs to be reconnected
   134  		p.mu.Lock()
   135  		defer p.mu.Unlock()
   136  
   137  		p.conns[pos], err = p.connFactory(p.host.String(), p.opts)
   138  		if err != nil {
   139  			return nil, err
   140  		}
   141  	}
   142  
   143  	return p.conns[pos], nil
   144  }
   145  
   146  // SetInitialPoolCap sets the initial capacity of the connection pool.
   147  //
   148  // Deprecated: This value should only be set when connecting
   149  func (p *Pool) SetInitialPoolCap(n int) {
   150  	return
   151  }
   152  
   153  // SetMaxIdleConns sets the maximum number of connections in the idle
   154  // connection pool.
   155  //
   156  // Deprecated: This value should only be set when connecting
   157  func (p *Pool) SetMaxIdleConns(n int) {
   158  	return
   159  }
   160  
   161  // SetMaxOpenConns sets the maximum number of open connections to the database.
   162  //
   163  // Deprecated: This value should only be set when connecting
   164  func (p *Pool) SetMaxOpenConns(n int) {
   165  	return
   166  }
   167  
   168  // Query execution functions
   169  
   170  // Exec executes a query without waiting for any response.
   171  func (p *Pool) Exec(ctx context.Context, q Query) error {
   172  	c, err := p.conn()
   173  	if err != nil {
   174  		return err
   175  	}
   176  
   177  	_, _, err = c.Query(ctx, q)
   178  	return err
   179  }
   180  
   181  // Query executes a query and waits for the response
   182  func (p *Pool) Query(ctx context.Context, q Query) (*Cursor, error) {
   183  	c, err := p.conn()
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  
   188  	_, cursor, err := c.Query(ctx, q)
   189  	return cursor, err
   190  }
   191  
   192  // Server returns the server name and server UUID being used by a connection.
   193  func (p *Pool) Server() (ServerResponse, error) {
   194  	var response ServerResponse
   195  
   196  	c, err := p.conn()
   197  	if err != nil {
   198  		return response, err
   199  	}
   200  
   201  	response, err = c.Server()
   202  	return response, err
   203  }