Ensure Client is safe for concurrent use

Fixes #6287.
pull/6288/head
Edd Robinson 2016-04-12 14:12:41 +01:00
parent a24f442b89
commit 592d668e1b
3 changed files with 58 additions and 3 deletions

View File

@ -16,6 +16,7 @@
- [#3773](https://github.com/influxdata/influxdb/issues/3773): Support empty tags for all WHERE equality operations.
- [#6270](https://github.com/influxdata/influxdb/issues/6270): tsm1 query engine alloc reduction
- [#6271](https://github.com/influxdata/influxdb/issues/6271): Fixed deadlock in tsm1 file store.
- [#6287](https://github.com/influxdata/influxdb/issues/6287): Fix data race in Influx Client.
## v0.12.0 [2016-04-05]
### Release Notes

View File

@ -90,7 +90,8 @@ type Client interface {
Close() error
}
// NewHTTPClient creates a client interface from the given config.
// NewHTTPClient returns a new Client from the provided config.
// Client is safe for concurrent use by multiple goroutines.
func NewHTTPClient(conf HTTPConfig) (Client, error) {
if conf.UserAgent == "" {
conf.UserAgent = "InfluxDBClient"
@ -114,7 +115,7 @@ func NewHTTPClient(conf HTTPConfig) (Client, error) {
tr.TLSClientConfig = conf.TLSConfig
}
return &client{
url: u,
url: *u,
username: conf.Username,
password: conf.Password,
useragent: conf.UserAgent,
@ -210,8 +211,12 @@ func (uc *udpclient) Close() error {
return uc.conn.Close()
}
// client is safe for concurrent use as the fields are all read-only
// once the client is instantiated.
type client struct {
url *url.URL
// N.B - if url.UserInfo is accessed in future modifications to the
// methods on client, you will need to syncronise access to url.
url url.URL
username string
password string
useragent string

View File

@ -6,6 +6,7 @@ import (
"net/http/httptest"
"reflect"
"strings"
"sync"
"testing"
"time"
)
@ -138,6 +139,54 @@ func TestClient_Ping(t *testing.T) {
}
}
func TestClient_Concurrent_Use(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{}`))
}))
defer ts.Close()
config := HTTPConfig{Addr: ts.URL}
c, _ := NewHTTPClient(config)
defer c.Close()
var wg sync.WaitGroup
wg.Add(3)
n := 1000
go func() {
defer wg.Done()
bp, err := NewBatchPoints(BatchPointsConfig{})
if err != nil {
t.Errorf("got error %v", err)
}
for i := 0; i < n; i++ {
if err = c.Write(bp); err != nil {
t.Fatalf("got error %v", err)
}
}
}()
go func() {
defer wg.Done()
var q Query
for i := 0; i < n; i++ {
if _, err := c.Query(q); err != nil {
t.Fatalf("got error %v", err)
}
}
}()
go func() {
defer wg.Done()
for i := 0; i < n; i++ {
c.Ping(time.Second)
}
}()
wg.Wait()
}
func TestClient_Write(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var data Response