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

     1  package rethinkdb
     2  
     3  import (
     4  	"crypto/tls"
     5  	"sync"
     6  	"time"
     7  
     8  	"golang.org/x/net/context"
     9  	p "gopkg.in/rethinkdb/rethinkdb-go.v6/ql2"
    10  )
    11  
    12  // A Session represents a connection to a RethinkDB cluster and should be used
    13  // when executing queries.
    14  type Session struct {
    15  	hosts []Host
    16  	opts  *ConnectOpts
    17  
    18  	mu      sync.RWMutex
    19  	cluster *Cluster
    20  	closed  bool
    21  }
    22  
    23  // ConnectOpts is used to specify optional arguments when connecting to a cluster.
    24  type ConnectOpts struct {
    25  	// Address holds the address of the server initially used when creating the
    26  	// session. Only used if Addresses is empty
    27  	Address string `rethinkdb:"address,omitempty" json:"address,omitempty"`
    28  	// Addresses holds the addresses of the servers initially used when creating
    29  	// the session.
    30  	Addresses []string `rethinkdb:"addresses,omitempty" json:"addresses,omitempty"`
    31  	// Database is the default database name used when executing queries, this
    32  	// value is only used if the query does not contain any DB term
    33  	Database string `rethinkdb:"database,omitempty" json:"database,omitempty"`
    34  	// Username holds the username used for authentication, if blank (and the v1
    35  	// handshake protocol is being used) then the admin user is used
    36  	Username string `rethinkdb:"username,omitempty" json:"username,omitempty"`
    37  	// Password holds the password used for authentication (only used when using
    38  	// the v1 handshake protocol)
    39  	Password string `rethinkdb:"password,omitempty" json:"password,omitempty"`
    40  	// AuthKey is used for authentication when using the v0.4 handshake protocol
    41  	// This field is no deprecated
    42  	AuthKey string `rethinkdb:"authkey,omitempty" json:"authkey,omitempty"`
    43  	// Timeout is the time the driver waits when creating new connections, to
    44  	// configure the timeout used when executing queries use WriteTimeout and
    45  	// ReadTimeout
    46  	Timeout time.Duration `rethinkdb:"timeout,omitempty" json:"timeout,omitempty"`
    47  	// WriteTimeout is the amount of time the driver will wait when sending the
    48  	// query to the server
    49  	// Deprecated: use RunOpts.Context instead
    50  	WriteTimeout time.Duration `rethinkdb:"write_timeout,omitempty" json:"write_timeout,omitempty"`
    51  	// ReadTimeout is the amount of time the driver will wait for a response from
    52  	// the server when executing queries.
    53  	// Deprecated: use RunOpts.Context instead
    54  	ReadTimeout time.Duration `rethinkdb:"read_timeout,omitempty" json:"read_timeout,omitempty"`
    55  	// KeepAlivePeriod is the keep alive period used by the connection, by default
    56  	// this is 30s. It is not possible to disable keep alive messages
    57  	KeepAlivePeriod time.Duration `rethinkdb:"keep_alive_timeout,omitempty" json:"keep_alive_timeout,omitempty"`
    58  	// TLSConfig holds the TLS configuration and can be used when connecting
    59  	// to a RethinkDB server protected by SSL
    60  	TLSConfig *tls.Config `rethinkdb:"tlsconfig,omitempty" json:"tlsconfig,omitempty"`
    61  	// HandshakeVersion is used to specify which handshake version should be
    62  	// used, this currently defaults to v1 which is used by RethinkDB 2.3 and
    63  	// later. If you are using an older version then you can set the handshake
    64  	// version to 0.4
    65  	HandshakeVersion HandshakeVersion `rethinkdb:"handshake_version,omitempty" json:"handshake_version,omitempty"`
    66  	// UseJSONNumber indicates whether the cursors running in this session should
    67  	// use json.Number instead of float64 while unmarshaling documents with
    68  	// interface{}. The default is `false`.
    69  	UseJSONNumber bool `json:"use_json_number,omitempty"`
    70  	// NumRetries is the number of times a query is retried if a connection
    71  	// error is detected, queries are not retried if RethinkDB returns a
    72  	// runtime error.
    73  	// Default is 3.
    74  	NumRetries int `json:"num_retries,omitempty"`
    75  
    76  	// InitialCap is used by the internal connection pool and is used to
    77  	// configure how many connections are created for each host when the
    78  	// session is created. If zero then no connections are created until
    79  	// the first query is executed.
    80  	InitialCap int `rethinkdb:"initial_cap,omitempty" json:"initial_cap,omitempty"`
    81  	// MaxOpen is used by the internal connection pool and is used to configure
    82  	// the maximum number of connections held in the pool. By default the
    83  	// maximum number of connections is 1
    84  	MaxOpen int `rethinkdb:"max_open,omitempty" json:"max_open,omitempty"`
    85  
    86  	// Below options are for cluster discovery, please note there is a high
    87  	// probability of these changing as the API is still being worked on.
    88  
    89  	// DiscoverHosts is used to enable host discovery, when true the driver
    90  	// will attempt to discover any new nodes added to the cluster and then
    91  	// start sending queries to these new nodes.
    92  	DiscoverHosts bool `rethinkdb:"discover_hosts,omitempty" json:"discover_hosts,omitempty"`
    93  	// HostDecayDuration is used by the go-hostpool package to calculate a weighted
    94  	// score when selecting a host. By default a value of 5 minutes is used.
    95  	HostDecayDuration time.Duration `json:"host_decay_duration,omitempty"`
    96  
    97  	// UseOpentracing is used to enable creating opentracing-go spans for queries.
    98  	// Each span is created as child of span from the context in `RunOpts`.
    99  	// This span lasts from point the query created to the point when cursor closed.
   100  	UseOpentracing bool `json:"use_opentracing,omitempty"`
   101  
   102  	// Deprecated: This function is no longer used due to changes in the
   103  	// way hosts are selected.
   104  	NodeRefreshInterval time.Duration `rethinkdb:"node_refresh_interval,omitempty" json:"node_refresh_interval,omitempty"`
   105  	// Deprecated: Use InitialCap instead
   106  	MaxIdle int `rethinkdb:"max_idle,omitempty" json:"max_idle,omitempty"`
   107  }
   108  
   109  func (o ConnectOpts) toMap() map[string]interface{} {
   110  	return optArgsToMap(o)
   111  }
   112  
   113  // Connect creates a new database session. To view the available connection
   114  // options see ConnectOpts.
   115  //
   116  // By default maxIdle and maxOpen are set to 1: passing values greater
   117  // than the default (e.g. MaxIdle: "10", MaxOpen: "20") will provide a
   118  // pool of re-usable connections.
   119  //
   120  // Basic connection example:
   121  //
   122  // 	session, err := r.Connect(r.ConnectOpts{
   123  // 		Address: "localhost:28015",
   124  // 		Database: "test",
   125  // 		AuthKey:  "14daak1cad13dj",
   126  // 	})
   127  //
   128  // Cluster connection example:
   129  //
   130  // 	session, err := r.Connect(r.ConnectOpts{
   131  // 		Addresses: []string{"localhost:28015", "localhost:28016"},
   132  // 		Database: "test",
   133  // 		AuthKey:  "14daak1cad13dj",
   134  // 	})
   135  func Connect(opts ConnectOpts) (*Session, error) {
   136  	var addresses = opts.Addresses
   137  	if len(addresses) == 0 {
   138  		addresses = []string{opts.Address}
   139  	}
   140  
   141  	hosts := make([]Host, len(addresses))
   142  	for i, address := range addresses {
   143  		hostname, port := splitAddress(address)
   144  		hosts[i] = NewHost(hostname, port)
   145  	}
   146  	if len(hosts) <= 0 {
   147  		return nil, ErrNoHosts
   148  	}
   149  
   150  	// Connect
   151  	s := &Session{
   152  		hosts: hosts,
   153  		opts:  &opts,
   154  	}
   155  
   156  	err := s.Reconnect()
   157  	if err != nil {
   158  		// note: s.Reconnect() will initialize cluster information which
   159  		// will cause the .IsConnected() method to be caught in a loop
   160  		return &Session{
   161  			hosts: hosts,
   162  			opts:  &opts,
   163  		}, err
   164  	}
   165  
   166  	return s, nil
   167  }
   168  
   169  // CloseOpts allows calls to the Close function to be configured.
   170  type CloseOpts struct {
   171  	NoReplyWait bool `rethinkdb:"noreplyWait,omitempty"`
   172  }
   173  
   174  func (o CloseOpts) toMap() map[string]interface{} {
   175  	return optArgsToMap(o)
   176  }
   177  
   178  // IsConnected returns true if session has a valid connection.
   179  func (s *Session) IsConnected() bool {
   180  	s.mu.RLock()
   181  	defer s.mu.RUnlock()
   182  
   183  	if s.cluster == nil || s.closed {
   184  		return false
   185  	}
   186  	return s.cluster.IsConnected()
   187  }
   188  
   189  // Reconnect closes and re-opens a session.
   190  func (s *Session) Reconnect(optArgs ...CloseOpts) error {
   191  	var err error
   192  
   193  	if err = s.Close(optArgs...); err != nil {
   194  		return err
   195  	}
   196  
   197  	s.mu.Lock()
   198  	s.cluster, err = NewCluster(s.hosts, s.opts)
   199  	if err != nil {
   200  		s.mu.Unlock()
   201  		return err
   202  	}
   203  
   204  	s.closed = false
   205  	s.mu.Unlock()
   206  
   207  	return nil
   208  }
   209  
   210  // Close closes the session
   211  func (s *Session) Close(optArgs ...CloseOpts) error {
   212  	s.mu.Lock()
   213  	defer s.mu.Unlock()
   214  
   215  	if s.closed {
   216  		return nil
   217  	}
   218  
   219  	if len(optArgs) >= 1 {
   220  		if optArgs[0].NoReplyWait {
   221  			s.mu.Unlock()
   222  			s.NoReplyWait()
   223  			s.mu.Lock()
   224  		}
   225  	}
   226  
   227  	if s.cluster != nil {
   228  		return s.cluster.Close()
   229  	}
   230  	s.cluster = nil
   231  	s.closed = true
   232  
   233  	return nil
   234  }
   235  
   236  // SetInitialPoolCap sets the initial capacity of the connection pool.
   237  func (s *Session) SetInitialPoolCap(n int) {
   238  	s.mu.Lock()
   239  	defer s.mu.Unlock()
   240  
   241  	s.opts.InitialCap = n
   242  	s.cluster.SetInitialPoolCap(n)
   243  }
   244  
   245  // SetMaxIdleConns sets the maximum number of connections in the idle
   246  // connection pool.
   247  func (s *Session) SetMaxIdleConns(n int) {
   248  	s.mu.Lock()
   249  	defer s.mu.Unlock()
   250  
   251  	s.opts.MaxIdle = n
   252  	s.cluster.SetMaxIdleConns(n)
   253  }
   254  
   255  // SetMaxOpenConns sets the maximum number of open connections to the database.
   256  func (s *Session) SetMaxOpenConns(n int) {
   257  	s.mu.Lock()
   258  	defer s.mu.Unlock()
   259  
   260  	s.opts.MaxOpen = n
   261  	s.cluster.SetMaxOpenConns(n)
   262  }
   263  
   264  // NoReplyWait ensures that previous queries with the noreply flag have been
   265  // processed by the server. Note that this guarantee only applies to queries
   266  // run on the given connection
   267  func (s *Session) NoReplyWait() error {
   268  	s.mu.RLock()
   269  	defer s.mu.RUnlock()
   270  
   271  	if s.closed {
   272  		return ErrConnectionClosed
   273  	}
   274  
   275  	return s.cluster.Exec(nil, Query{ // nil = connection opts' defaults
   276  		Type: p.Query_NOREPLY_WAIT,
   277  	})
   278  }
   279  
   280  // Use changes the default database used
   281  func (s *Session) Use(database string) {
   282  	s.mu.Lock()
   283  	defer s.mu.Unlock()
   284  
   285  	s.opts.Database = database
   286  }
   287  
   288  // Database returns the selected database set by Use
   289  func (s *Session) Database() string {
   290  	s.mu.RLock()
   291  	defer s.mu.RUnlock()
   292  
   293  	return s.opts.Database
   294  }
   295  
   296  // Query executes a ReQL query using the session to connect to the database
   297  func (s *Session) Query(ctx context.Context, q Query) (*Cursor, error) {
   298  	s.mu.RLock()
   299  	defer s.mu.RUnlock()
   300  
   301  	if s.closed {
   302  		return nil, ErrConnectionClosed
   303  	}
   304  
   305  	return s.cluster.Query(ctx, q)
   306  }
   307  
   308  // Exec executes a ReQL query using the session to connect to the database
   309  func (s *Session) Exec(ctx context.Context, q Query) error {
   310  	s.mu.RLock()
   311  	defer s.mu.RUnlock()
   312  
   313  	if s.closed {
   314  		return ErrConnectionClosed
   315  	}
   316  
   317  	return s.cluster.Exec(ctx, q)
   318  }
   319  
   320  // Server returns the server name and server UUID being used by a connection.
   321  func (s *Session) Server() (ServerResponse, error) {
   322  	return s.cluster.Server()
   323  }
   324  
   325  // SetHosts resets the hosts used when connecting to the RethinkDB cluster
   326  func (s *Session) SetHosts(hosts []Host) {
   327  	s.mu.Lock()
   328  	defer s.mu.Unlock()
   329  
   330  	s.hosts = hosts
   331  }
   332  
   333  func (s *Session) newQuery(t Term, opts map[string]interface{}) (Query, error) {
   334  	return newQuery(t, opts, s.opts)
   335  }