Skip to content

Commit 896bbb8

Browse files
authored
Merge Development (#79)
Includes: * Reduced memory in bulk operations (#56) * Native x509 authentication (#55) * Better connection recovery (#69) * Example usage (#75 and #78) Thanks to: * @bachue * @csucu * @feliixx --- [Throughput overview](https://user-images.githubusercontent.com/9275968/34954403-3d3253dc-fa18-11e7-8eef-0f2b0f21edc3.png) Select throughput has increased by ~600 requests/second with slightly increased variance: ``` x => r2017.11.06-select-zipfian-throughput.log y => 9acbd68-select-zipfian-throughput.log n min max median average stddev p99 x 3600 49246 71368 66542 66517.26 2327.675 70927.01 y 3600 53304 72005 67151 67145.36 2448.534 71630.00 62000 64000 66000 68000 70000 72000 |----------+-----------+-----------+------------+-----------+-----------+-----| +---------+--------+ 1 -------------------| | |-------------------- +---------+--------+ +---------+---------+ 2 ----------------------------| | |-------------------- +---------+---------+ Legend: 1=data$x, 2=data$y At 95% probablitiy: ===> average is statistically significant (p=0.000000, diff ~628.094444) ===> variance is statistically significant (p=0.002398) ``` * [insert-latency.txt](https://github.com/globalsign/mgo/files/1632474/insert-latency.txt) * [insert-throughput.txt](https://github.com/globalsign/mgo/files/1632475/insert-throughput.txt) * [select-zipfian-latency.txt](https://github.com/globalsign/mgo/files/1632476/select-zipfian-latency.txt) * [select-zipfian-throughput.txt](https://github.com/globalsign/mgo/files/1632477/select-zipfian-throughput.txt) * [update-zipfian-latency.txt](https://github.com/globalsign/mgo/files/1632478/update-zipfian-latency.txt) * [update-zipfian-throughput.txt](https://github.com/globalsign/mgo/files/1632479/update-zipfian-throughput.txt) Note: latencies are approximations calculated from grouped data
1 parent 5be15cc commit 896bbb8

File tree

14 files changed

+534
-163
lines changed

14 files changed

+534
-163
lines changed

.travis.yml

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,23 @@ language: go
22

33
go_import_path: github.com/globalsign/mgo
44

5+
go:
6+
- 1.8.x
7+
- 1.9.x
8+
59
env:
610
global:
711
- BUCKET=https://s3.eu-west-2.amazonaws.com/globalsign-mgo
12+
- FASTDL=https://fastdl.mongodb.org/linux
813
matrix:
9-
- GO=1.7 MONGODB=x86_64-2.6.11
10-
- GO=1.8.x MONGODB=x86_64-2.6.11
11-
- GO=1.7 MONGODB=x86_64-3.0.9
12-
- GO=1.8.x MONGODB=x86_64-3.0.9
13-
- GO=1.7 MONGODB=x86_64-3.2.3-nojournal
14-
- GO=1.8.x MONGODB=x86_64-3.2.3-nojournal
15-
- GO=1.7 MONGODB=x86_64-3.2.12
16-
- GO=1.8.x MONGODB=x86_64-3.2.12
17-
- GO=1.7 MONGODB=x86_64-3.2.16
18-
- GO=1.8.x MONGODB=x86_64-3.2.16
19-
- GO=1.7 MONGODB=x86_64-3.4.8
20-
- GO=1.8.x MONGODB=x86_64-3.4.8
14+
- MONGODB=x86_64-ubuntu1404-3.0.15
15+
- MONGODB=x86_64-ubuntu1404-3.2.17
16+
- MONGODB=x86_64-ubuntu1404-3.4.10
17+
- MONGODB=x86_64-ubuntu1404-3.6.0
2118

2219
install:
23-
- eval "$(gimme $GO)"
2420

25-
- wget $BUCKET/mongodb-linux-$MONGODB.tgz
21+
- wget $FASTDL/mongodb-linux-$MONGODB.tgz
2622
- tar xzvf mongodb-linux-$MONGODB.tgz
2723
- export PATH=$PWD/mongodb-linux-$MONGODB/bin:$PATH
2824

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Further PR's (with tests) are welcome, but please maintain backwards compatibili
1111
* Fixes attempting to authenticate before every query ([details](https://github.com/go-mgo/mgo/issues/254))
1212
* Removes bulk update / delete batch size limitations ([details](https://github.com/go-mgo/mgo/issues/288))
1313
* Adds native support for `time.Duration` marshalling ([details](https://github.com/go-mgo/mgo/pull/373))
14-
* Reduce memory footprint / garbage collection pressure by reusing buffers ([details](https://github.com/go-mgo/mgo/pull/229))
14+
* Reduce memory footprint / garbage collection pressure by reusing buffers ([details](https://github.com/go-mgo/mgo/pull/229), [more](https://github.com/globalsign/mgo/pull/56))
1515
* Support majority read concerns ([details](https://github.com/globalsign/mgo/pull/2))
1616
* Improved connection handling ([details](https://github.com/globalsign/mgo/pull/5))
1717
* Hides SASL warnings ([details](https://github.com/globalsign/mgo/pull/7))
@@ -32,10 +32,13 @@ Further PR's (with tests) are welcome, but please maintain backwards compatibili
3232
* GetBSON correctly handles structs with both fields and pointers ([details](https://github.com/globalsign/mgo/pull/40))
3333
* Improved bson.Raw unmarshalling performance ([details](https://github.com/globalsign/mgo/pull/49))
3434
* Minimise socket connection timeouts due to excessive locking ([details](https://github.com/globalsign/mgo/pull/52))
35+
* Natively support X509 client authentication ([details](https://github.com/globalsign/mgo/pull/55))
36+
* Gracefully recover from a temporarily unreachable server ([details](https://github.com/globalsign/mgo/pull/69))
3537

3638
---
3739

3840
### Thanks to
41+
* @bachue
3942
* @bozaro
4043
* @BenLubar
4144
* @carter2000

auth_test.go

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ package mgo_test
2828

2929
import (
3030
"crypto/tls"
31+
"crypto/x509"
3132
"flag"
3233
"fmt"
3334
"io/ioutil"
@@ -38,7 +39,7 @@ import (
3839
"sync"
3940
"time"
4041

41-
mgo "github.com/globalsign/mgo"
42+
"github.com/globalsign/mgo"
4243
. "gopkg.in/check.v1"
4344
)
4445

@@ -963,6 +964,73 @@ func (s *S) TestAuthX509Cred(c *C) {
963964
c.Assert(len(names) > 0, Equals, true)
964965
}
965966

967+
func (s *S) TestAuthX509CredRDNConstruction(c *C) {
968+
session, err := mgo.Dial("localhost:40001")
969+
c.Assert(err, IsNil)
970+
defer session.Close()
971+
binfo, err := session.BuildInfo()
972+
c.Assert(err, IsNil)
973+
if binfo.OpenSSLVersion == "" {
974+
c.Skip("server does not support SSL")
975+
}
976+
977+
clientCertPEM, err := ioutil.ReadFile("harness/certs/client.pem")
978+
c.Assert(err, IsNil)
979+
980+
clientCert, err := tls.X509KeyPair(clientCertPEM, clientCertPEM)
981+
c.Assert(err, IsNil)
982+
983+
clientCert.Leaf, err = x509.ParseCertificate(clientCert.Certificate[0])
984+
c.Assert(err, IsNil)
985+
986+
tlsConfig := &tls.Config{
987+
InsecureSkipVerify: true,
988+
Certificates: []tls.Certificate{clientCert},
989+
}
990+
991+
var host = "localhost:40003"
992+
c.Logf("Connecting to %s...", host)
993+
session, err = mgo.DialWithInfo(&mgo.DialInfo{
994+
Addrs: []string{host},
995+
DialServer: func(addr *mgo.ServerAddr) (net.Conn, error) {
996+
return tls.Dial("tcp", addr.String(), tlsConfig)
997+
},
998+
})
999+
c.Assert(err, IsNil)
1000+
defer session.Close()
1001+
1002+
cred := &mgo.Credential{
1003+
Username: "root",
1004+
Mechanism: "MONGODB-X509",
1005+
Source: "$external",
1006+
Certificate: tlsConfig.Certificates[0].Leaf,
1007+
}
1008+
err = session.Login(cred)
1009+
c.Assert(err, NotNil)
1010+
1011+
err = session.Login(&mgo.Credential{Username: "root", Password: "rapadura"})
1012+
c.Assert(err, IsNil)
1013+
1014+
// This needs to be kept in sync with client.pem
1015+
x509Subject := "CN=localhost,OU=Client,O=MGO,L=MGO,ST=MGO,C=GO"
1016+
1017+
externalDB := session.DB("$external")
1018+
var x509User = mgo.User{
1019+
Username: x509Subject,
1020+
OtherDBRoles: map[string][]mgo.Role{"admin": {mgo.RoleRoot}},
1021+
}
1022+
err = externalDB.UpsertUser(&x509User)
1023+
c.Assert(err, IsNil)
1024+
1025+
session.LogoutAll()
1026+
1027+
cred.Username = ""
1028+
c.Logf("Authenticating...")
1029+
err = session.Login(cred)
1030+
c.Assert(err, IsNil)
1031+
c.Logf("Authenticated!")
1032+
}
1033+
9661034
var (
9671035
plainFlag = flag.String("plain", "", "Host to test PLAIN authentication against (depends on custom environment)")
9681036
plainUser = "einstein"

bulk.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package mgo
33
import (
44
"bytes"
55
"sort"
6+
"sync"
67

78
"github.com/globalsign/mgo/bson"
89
)
@@ -118,6 +119,15 @@ func (e *BulkError) Cases() []BulkErrorCase {
118119
return e.ecases
119120
}
120121

122+
var actionPool = sync.Pool{
123+
New: func() interface{} {
124+
return &bulkAction{
125+
docs: make([]interface{}, 0),
126+
idxs: make([]int, 0),
127+
}
128+
},
129+
}
130+
121131
// Bulk returns a value to prepare the execution of a bulk operation.
122132
func (c *Collection) Bulk() *Bulk {
123133
return &Bulk{c: c, ordered: true}
@@ -145,7 +155,9 @@ func (b *Bulk) action(op bulkOp, opcount int) *bulkAction {
145155
}
146156
}
147157
if action == nil {
148-
b.actions = append(b.actions, bulkAction{op: op})
158+
a := actionPool.Get().(*bulkAction)
159+
a.op = op
160+
b.actions = append(b.actions, *a)
149161
action = &b.actions[len(b.actions)-1]
150162
}
151163
for i := 0; i < opcount; i++ {
@@ -288,6 +300,9 @@ func (b *Bulk) Run() (*BulkResult, error) {
288300
default:
289301
panic("unknown bulk operation")
290302
}
303+
action.idxs = action.idxs[0:0]
304+
action.docs = action.docs[0:0]
305+
actionPool.Put(action)
291306
if !ok {
292307
failed = true
293308
if b.ordered {

cluster_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2083,8 +2083,8 @@ func (s *S) TestDoNotFallbackToMonotonic(c *C) {
20832083
if !s.versionAtLeast(3, 0) {
20842084
c.Skip("command-counting logic depends on 3.0+")
20852085
}
2086-
if s.versionAtLeast(3, 4) {
2087-
c.Skip("failing on 3.4+")
2086+
if s.versionAtLeast(3, 2, 17) {
2087+
c.Skip("failing on 3.2.17+")
20882088
}
20892089

20902090
session, err := mgo.Dial("localhost:40012")

example_test.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package mgo
2+
3+
import (
4+
"crypto/tls"
5+
"crypto/x509"
6+
"io/ioutil"
7+
"net"
8+
"sync"
9+
)
10+
11+
func ExampleCredential_x509Authentication() {
12+
// MongoDB follows RFC2253 for the ordering of the DN - if the order is
13+
// incorrect when creating the user in Mongo, the client will not be able to
14+
// connect.
15+
//
16+
// The best way to generate the DN with the correct ordering is with
17+
// openssl:
18+
//
19+
// openssl x509 -in client.crt -inform PEM -noout -subject -nameopt RFC2253
20+
// subject= CN=Example App,OU=MongoDB Client Authentication,O=GlobalSign,C=GB
21+
//
22+
//
23+
// And then create the user in MongoDB with the above DN:
24+
//
25+
// db.getSiblingDB("$external").runCommand({
26+
// createUser: "CN=Example App,OU=MongoDB Client Authentication,O=GlobalSign,C=GB",
27+
// roles: [
28+
// { role: 'readWrite', db: 'bananas' },
29+
// { role: 'userAdminAnyDatabase', db: 'admin' }
30+
// ],
31+
// writeConcern: { w: "majority" , wtimeout: 5000 }
32+
// })
33+
//
34+
//
35+
// References:
36+
// - https://docs.mongodb.com/manual/tutorial/configure-x509-client-authentication/
37+
// - https://docs.mongodb.com/manual/core/security-x.509/
38+
//
39+
40+
// Read in the PEM encoded X509 certificate.
41+
//
42+
// See the client.pem file at the path below.
43+
clientCertPEM, err := ioutil.ReadFile("harness/certs/client.pem")
44+
45+
// Read in the PEM encoded private key.
46+
clientKeyPEM, err := ioutil.ReadFile("harness/certs/client.key")
47+
48+
// Parse the private key, and the public key contained within the
49+
// certificate.
50+
clientCert, err := tls.X509KeyPair(clientCertPEM, clientKeyPEM)
51+
52+
// Parse the actual certificate data
53+
clientCert.Leaf, err = x509.ParseCertificate(clientCert.Certificate[0])
54+
55+
// Use the cert to set up a TLS connection to Mongo
56+
tlsConfig := &tls.Config{
57+
Certificates: []tls.Certificate{clientCert},
58+
59+
// This is set to true so the example works within the test
60+
// environment.
61+
//
62+
// DO NOT set InsecureSkipVerify to true in a production
63+
// environment - if you use an untrusted CA/have your own, load
64+
// its certificate into the RootCAs value instead.
65+
//
66+
// RootCAs: myCAChain,
67+
InsecureSkipVerify: true,
68+
}
69+
70+
// Connect to Mongo using TLS
71+
host := "localhost:40003"
72+
session, err := DialWithInfo(&DialInfo{
73+
Addrs: []string{host},
74+
DialServer: func(addr *ServerAddr) (net.Conn, error) {
75+
return tls.Dial("tcp", host, tlsConfig)
76+
},
77+
})
78+
79+
// Authenticate using the certificate
80+
cred := &Credential{Certificate: tlsConfig.Certificates[0].Leaf}
81+
if err := session.Login(cred); err != nil {
82+
panic(err)
83+
}
84+
85+
// Done! Use mgo as normal from here.
86+
//
87+
// You should actually check the error code at each step.
88+
_ = err
89+
}
90+
91+
func ExampleSession_concurrency() {
92+
// This example shows the best practise for concurrent use of a mgo session.
93+
//
94+
// Internally mgo maintains a connection pool, dialling new connections as
95+
// required.
96+
//
97+
// Some general suggestions:
98+
// - Define a struct holding the original session, database name and
99+
// collection name instead of passing them explicitly.
100+
// - Define an interface abstracting your data access instead of exposing
101+
// mgo to your application code directly.
102+
// - Limit concurrency at the application level, not with SetPoolLimit().
103+
104+
// This will be our concurrent worker
105+
var doStuff = func(wg *sync.WaitGroup, session *Session) {
106+
defer wg.Done()
107+
108+
// Copy the session - if needed this will dial a new connection which
109+
// can later be reused.
110+
//
111+
// Calling close returns the connection to the pool.
112+
conn := session.Copy()
113+
defer conn.Close()
114+
115+
// Do something(s) with the connection
116+
_, _ = conn.DB("").C("my_data").Count()
117+
}
118+
119+
///////////////////////////////////////////////
120+
121+
// Dial a connection to Mongo - this creates the connection pool
122+
session, err := Dial("localhost:40003/my_database")
123+
if err != nil {
124+
panic(err)
125+
}
126+
127+
// Concurrently do things, passing the session to the worker
128+
wg := &sync.WaitGroup{}
129+
for i := 0; i < 10; i++ {
130+
wg.Add(1)
131+
go doStuff(wg, session)
132+
}
133+
wg.Wait()
134+
135+
session.Close()
136+
}

harness/daemons/.env

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,8 @@ versionAtLeast() {
2222

2323
COMMONDOPTSNOIP="
2424
--nohttpinterface
25-
--noprealloc
2625
--nojournal
2726
--smallfiles
28-
--nssize=1
2927
--oplogSize=1
3028
--dbpath ./db
3129
"
@@ -55,14 +53,12 @@ if versionAtLeast 3 2; then
5553
# 3.2 doesn't like --nojournal on config servers.
5654
COMMONCOPTS="$(echo "$COMMONCOPTS" | sed '/--nojournal/d')"
5755

58-
5956
if versionAtLeast 3 4; then
6057
# http interface is disabled by default, this option does not exist anymore
6158
COMMONDOPTSNOIP="$(echo "$COMMONDOPTSNOIP" | sed '/--nohttpinterface/d')"
6259
COMMONDOPTS="$(echo "$COMMONDOPTS" | sed '/--nohttpinterface/d')"
6360
COMMONCOPTS="$(echo "$COMMONCOPTS" | sed '/--nohttpinterface/d')"
6461

65-
6662
# config server need to be started as replica set
6763
CFG1OPTS="--replSet conf1"
6864
CFG2OPTS="--replSet conf2"
@@ -71,12 +67,6 @@ if versionAtLeast 3 2; then
7167
MONGOS1OPTS="--configdb conf1/127.0.0.1:40101"
7268
MONGOS2OPTS="--configdb conf2/127.0.0.1:40102"
7369
MONGOS3OPTS="--configdb conf3/127.0.0.1:40103"
74-
else
75-
76-
# Go back to MMAPv1 so it's not super sluggish. :-(
77-
COMMONDOPTSNOIP="--storageEngine=mmapv1 $COMMONDOPTSNOIP"
78-
COMMONDOPTS="--storageEngine=mmapv1 $COMMONDOPTS"
79-
COMMONCOPTS="--storageEngine=mmapv1 $COMMONCOPTS"
8070
fi
8171
fi
8272

harness/daemons/db2/run

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,5 @@
33
. ../.env
44

55
exec mongod $COMMONDOPTS \
6-
--shardsvr \
76
--port 40002 \
87
--auth

harness/daemons/db3/run

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
. ../.env
44

55
exec mongod $COMMONDOPTS \
6-
--shardsvr \
76
--port 40003 \
87
--auth \
98
--sslMode preferSSL \

0 commit comments

Comments
 (0)