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 }