Skip to content

Commit

Permalink
Merge branch 'release/v2.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
dancannon committed Jun 26, 2016
2 parents de072a9 + c8df9e3 commit 61cfdef
Show file tree
Hide file tree
Showing 12 changed files with 733 additions and 90 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,26 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## v2.1.0 - 2016-06-26

### Added

- Added ability to mock queries based on the library github.com/stretchr/testify
+ Added the `QueryExecutor` interface and changed query runner methods (`Run`/`Exec`) to accept this type instead of `*Session`, `Session` will still be accepted as it implements the `QueryExecutor` interface.
+ Added the `NewMock` function to create a mock query executor
+ Queries can be mocked using `On` and `Return`, `Mock` also contains functions for asserting that the required mocked queries were executed.
+ For more information about how to mock queries see the readme and tests in `mock_test.go`.

## Changed

- Exported the `Build()` function on `Query` and `Term`.
- Updated import of `github.com/cenkalti/backoff` to `github.com/cenk/backoff`

## v2.0.4 - 2016-05-22

### Changed
- Changed `Connect` to return the reason for connections failing (instead of just "no connections were made when creating the session")
- Changed how queries are retried internally, previously when a query failed due to an issue with the connection a new connection was picked from the connection pool and the query was retried, now the driver will attempt to retry the query with a new host (and connection). This should make applications connecting to a multi-node cluster more reliable.

### Fixed
- Fixed queries not being retried when using `Query()`, queries are now retried if the request failed due to a bad connection.
Expand Down
37 changes: 36 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

![GoRethink Logo](https://raw.github.com/wiki/dancannon/gorethink/gopher-and-thinker-s.png "Golang Gopher and RethinkDB Thinker")

Current version: v2.0.4 (RethinkDB v2.3)
Current version: v2.1.0 (RethinkDB v2.3)

Please note that this version of the driver only supports versions of RethinkDB using the v0.4 protocol (any versions of the driver older than RethinkDB 2.0 will not work).

Expand Down Expand Up @@ -297,6 +297,41 @@ Alternatively if you wish to modify the logging behaviour you can modify the log
r.Log.Out = ioutil.Discard
```

## Mocking

The driver includes the ability to mock queries meaning that you can test your code without needing to talk to a real RethinkDB cluster, this is perfect for ensuring that your application has high unit test coverage.

To write tests with mocking you should create an instance of `Mock` and then setup expectations using `On` and `Return`. Expectations allow you to define what results should be returned when a known query is executed, they are configured by passing the query term you want to mock to `On` and then the response and error to `Return`, if a non-nil error is passed to `Return` then any time that query is executed the error will be returned, if no error is passed then a cursor will be built using the value passed to `Return`. Once all your expectations have been created you should then execute you queries using the `Mock` instead of a `Session`.

Here is an example that shows how to mock a query that returns multiple rows and the resulting cursor can be used as normal.

```go
func TestSomething(t *testing.T) {
mock := r.NewMock()
mock.on(r.Table("people")).Return([]interface{}{
map[string]interface{}{"id": 1, "name": "John Smith"},
map[string]interface{}{"id": 2, "name": "Jane Smith"},
}, nil)

cursor, err := r.Table("people").Run(mock)
if err != nil {
t.Errorf(err)
}

var rows []interface{}
err := res.All(&rows)
if err != nil {
t.Errorf(err)
}

// Test result of rows

mock.AssertExpectations(t)
}
```

The mocking implementation is based on amazing https://github.com/stretchr/testify library, thanks to @stretchr for their awesome work!

## Benchmarks

Everyone wants their project's benchmarks to be speedy. And while we know that rethinkDb and the gorethink driver are quite fast, our primary goal is for our benchmarks to be correct. They are designed to give you, the user, an accurate picture of writes per second (w/s). If you come up with a accurate test that meets this aim, submit a pull request please.
Expand Down
71 changes: 55 additions & 16 deletions cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"time"

"github.com/Sirupsen/logrus"
"github.com/cenkalti/backoff"
"github.com/cenk/backoff"
"github.com/hailocab/go-hostpool"
)

Expand Down Expand Up @@ -58,37 +58,68 @@ func NewCluster(hosts []Host, opts *ConnectOpts) (*Cluster, error) {

// Query executes a ReQL query using the cluster to connect to the database
func (c *Cluster) Query(q Query) (cursor *Cursor, err error) {
node, hpr, err := c.GetNextNode()
if err != nil {
return nil, err
for i := 0; i < c.numRetries(); i++ {
var node *Node
var hpr hostpool.HostPoolResponse

node, hpr, err = c.GetNextNode()
if err != nil {
return nil, err
}

cursor, err = node.Query(q)
hpr.Mark(err)

if !shouldRetryQuery(q, err) {
break
}
}

cursor, err = node.Query(q)
hpr.Mark(err)
return cursor, err
}

// Exec executes a ReQL query using the cluster to connect to the database
func (c *Cluster) Exec(q Query) (err error) {
node, hpr, err := c.GetNextNode()
if err != nil {
return err
for i := 0; i < c.numRetries(); i++ {
var node *Node
var hpr hostpool.HostPoolResponse

node, hpr, err = c.GetNextNode()
if err != nil {
return err
}

err = node.Exec(q)
hpr.Mark(err)

if !shouldRetryQuery(q, err) {
break
}
}

err = node.Exec(q)
hpr.Mark(err)
return err
}

// Server returns the server name and server UUID being used by a connection.
func (c *Cluster) Server() (response ServerResponse, err error) {
node, hpr, err := c.GetNextNode()
if err != nil {
return ServerResponse{}, err
for i := 0; i < c.numRetries(); i++ {
var node *Node
var hpr hostpool.HostPoolResponse

node, hpr, err = c.GetNextNode()
if err != nil {
return ServerResponse{}, err
}

response, err = node.Server()
hpr.Mark(err)

// This query should not fail so retry if any error is detected
if err == nil {
break
}
}

response, err = node.Server()
hpr.Mark(err)
return response, err
}

Expand Down Expand Up @@ -473,3 +504,11 @@ func (c *Cluster) removeNode(nodeID string) {
func (c *Cluster) nextNodeIndex() int64 {
return atomic.AddInt64(&c.nodeIndex, 1)
}

func (c *Cluster) numRetries() int {
if n := c.opts.NumRetries; n > 0 {
return n
}

return 3
}
4 changes: 2 additions & 2 deletions connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func (c *Connection) Query(q Query) (*Response, *Cursor, error) {
if q.Type == p.Query_START || q.Type == p.Query_NOREPLY_WAIT {
if c.opts.Database != "" {
var err error
q.Opts["db"], err = DB(c.opts.Database).build()
q.Opts["db"], err = DB(c.opts.Database).Build()
if err != nil {
c.mu.Unlock()
return nil, nil, RQLDriverError{rqlError(err.Error())}
Expand Down Expand Up @@ -190,7 +190,7 @@ func (c *Connection) Server() (ServerResponse, error) {
// sendQuery marshals the Query and sends the JSON to the server.
func (c *Connection) sendQuery(q Query) error {
// Build query
b, err := json.Marshal(q.build())
b, err := json.Marshal(q.Build())
if err != nil {
return RQLDriverError{rqlError("Error building query")}
}
Expand Down
2 changes: 1 addition & 1 deletion doc.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Package gorethink implements a Go driver for RethinkDB
//
// Current version: v2.0.4 (RethinkDB v2.3)
// Current version: v2.1.0 (RethinkDB v2.3)
// For more in depth information on how to use RethinkDB check out the API docs
// at http://rethinkdb.com/api
package gorethink
Loading

0 comments on commit 61cfdef

Please sign in to comment.