Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

send metadata during handshake #28

Merged
merged 1 commit into from
Sep 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"errors"
"fmt"
"net"
"runtime"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -61,16 +62,18 @@ type mongoCluster struct {
cachedIndex map[string]bool
sync chan bool
dial dialer
appName string
}

func newCluster(userSeeds []string, direct, failFast bool, dial dialer, setName string) *mongoCluster {
func newCluster(userSeeds []string, direct, failFast bool, dial dialer, setName string, appName string) *mongoCluster {
cluster := &mongoCluster{
userSeeds: userSeeds,
references: 1,
direct: direct,
failFast: failFast,
dial: dial,
setName: setName,
appName: appName,
}
cluster.serverSynced.L = cluster.RWMutex.RLocker()
cluster.sync = make(chan bool, 1)
Expand Down Expand Up @@ -144,7 +147,17 @@ func (cluster *mongoCluster) isMaster(socket *mongoSocket, result *isMasterResul
// Monotonic let's it talk to a slave and still hold the socket.
session := newSession(Monotonic, cluster, 10*time.Second)
session.setSocket(socket)
err := session.Run("ismaster", result)

// provide some meta infos on the client,
// see https://github.com/mongodb/specifications/blob/master/source/mongodb-handshake/handshake.rst#connection-handshake
// for details
metaInfo := bson.M{"driver": bson.M{"name": "mgo", "version": "globalsign"},
"os": bson.M{"type": runtime.GOOS, "architecture": runtime.GOARCH}}

if cluster.appName != "" {
metaInfo["application"] = bson.M{"name": cluster.appName}
}
err := session.Run(bson.D{{"isMaster", 1}, {"client", metaInfo}}, result)
session.Close()
return err
}
Expand Down
16 changes: 15 additions & 1 deletion session.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,10 @@ const (
// Defines the per-server socket pool limit. Defaults to 4096.
// See Session.SetPoolLimit for details.
//
// appName=<appName>
//
// The identifier of the client application which ran the operation. This
// param can't exceed 128 bytes
//
// Relevant documentation:
//
Expand Down Expand Up @@ -279,6 +283,7 @@ func ParseURL(url string) (*DialInfo, error) {
source := ""
setName := ""
poolLimit := 0
appName := ""
readPreferenceMode := Primary
var readPreferenceTagSets []bson.D
for _, opt := range uinfo.options {
Expand All @@ -296,6 +301,11 @@ func ParseURL(url string) (*DialInfo, error) {
if err != nil {
return nil, errors.New("bad value for maxPoolSize: " + opt.value)
}
case "appName":
if len(opt.value) > 128 {
return nil, errors.New("appName too long, must be < 128 bytes: " + opt.value)
}
appName = opt.value
case "readPreference":
switch opt.value {
case "nearest":
Expand Down Expand Up @@ -350,6 +360,7 @@ func ParseURL(url string) (*DialInfo, error) {
Service: service,
Source: source,
PoolLimit: poolLimit,
AppName: appName,
ReadPreference: &ReadPreference{
Mode: readPreferenceMode,
TagSets: readPreferenceTagSets,
Expand Down Expand Up @@ -409,6 +420,9 @@ type DialInfo struct {
// See Session.SetPoolLimit for details.
PoolLimit int

// The identifier of the client application which ran the operation.
AppName string

// ReadPreference defines the manner in which servers are chosen. See
// Session.SetMode and Session.SelectServers.
ReadPreference *ReadPreference
Expand Down Expand Up @@ -472,7 +486,7 @@ func DialWithInfo(info *DialInfo) (*Session, error) {
}
addrs[i] = addr
}
cluster := newCluster(addrs, info.Direct, info.FailFast, dialer{info.Dial, info.DialServer}, info.ReplicaSetName)
cluster := newCluster(addrs, info.Direct, info.FailFast, dialer{info.Dial, info.DialServer}, info.ReplicaSetName, info.AppName)
session := newSession(Eventual, cluster, info.Timeout)
session.defaultdb = info.Database
if session.defaultdb == "" {
Expand Down
44 changes: 44 additions & 0 deletions session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,50 @@ func (s *S) TestURLInvalidReadPreferenceTags(c *C) {
}
}

func (s *S) TestURLWithAppName(c *C) {
if !s.versionAtLeast(3, 4) {
c.Skip("appName depends on MongoDB 3.4+")
}
appName := "myAppName"
session, err := mgo.Dial("localhost:40001?appName=" + appName)
c.Assert(err, IsNil)
defer session.Close()

db := session.DB("mydb")

err = db.Run(bson.D{{"profile", 2}}, nil)
c.Assert(err, IsNil)

coll := db.C("mycoll")
err = coll.Insert(M{"a": 1, "b": 2})
c.Assert(err, IsNil)

result := struct{ A, B int }{}
err = coll.Find(M{"a": 1}).One(&result)
c.Assert(err, IsNil)

profileResult := struct {
AppName string `bson:"appName"`
}{}

err = db.C("system.profile").Find(nil).Sort("-ts").One(&profileResult)
c.Assert(err, IsNil)
c.Assert(appName, Equals, profileResult.AppName)
// reset profiling to 0 as it add unecessary overhead to all other test
err = db.Run(bson.D{{"profile", 0}}, nil)
c.Assert(err, IsNil)
}

func (s *S) TestURLWithAppNameTooLong(c *C) {
if !s.versionAtLeast(3, 4) {
c.Skip("appName depends on MongoDB 3.4+")
}
appName := "myAppNameWayTooLongmyAppNameWayTooLongmyAppNameWayTooLongmyAppNameWayTooLong"
appName += appName
_, err := mgo.Dial("localhost:40001?appName=" + appName)
c.Assert(err, ErrorMatches, "appName too long, must be < 128 bytes: "+appName)
}

func (s *S) TestInsertFindOne(c *C) {
session, err := mgo.Dial("localhost:40001")
c.Assert(err, IsNil)
Expand Down