@@ -14,24 +14,28 @@ import (
14
14
"github.com/mongodb/mongo-go-driver/core/connstring"
15
15
"github.com/mongodb/mongo-go-driver/core/description"
16
16
"github.com/mongodb/mongo-go-driver/core/dispatch"
17
- "github.com/mongodb/mongo-go-driver/core/option"
18
17
"github.com/mongodb/mongo-go-driver/core/readconcern"
19
18
"github.com/mongodb/mongo-go-driver/core/readpref"
19
+ "github.com/mongodb/mongo-go-driver/core/session"
20
20
"github.com/mongodb/mongo-go-driver/core/tag"
21
21
"github.com/mongodb/mongo-go-driver/core/topology"
22
+ "github.com/mongodb/mongo-go-driver/core/uuid"
22
23
"github.com/mongodb/mongo-go-driver/core/writeconcern"
23
24
"github.com/mongodb/mongo-go-driver/mongo/clientopt"
24
25
"github.com/mongodb/mongo-go-driver/mongo/dbopt"
26
+ "github.com/mongodb/mongo-go-driver/mongo/listdbopt"
25
27
)
26
28
27
29
const defaultLocalThreshold = 15 * time .Millisecond
28
30
29
31
// Client performs operations on a given topology.
30
32
type Client struct {
33
+ id uuid.UUID
31
34
topologyOptions []topology.Option
32
35
topology * topology.Topology
33
36
connString connstring.ConnString
34
37
localThreshold time.Duration
38
+ clock * session.ClusterClock
35
39
readPreference * readpref.ReadPref
36
40
readConcern * readconcern.ReadConcern
37
41
writeConcern * writeconcern.WriteConcern
@@ -81,7 +85,13 @@ func NewClientFromConnString(cs connstring.ConnString) (*Client, error) {
81
85
// Connect initializes the Client by starting background monitoring goroutines.
82
86
// This method must be called before a Client can be used.
83
87
func (c * Client ) Connect (ctx context.Context ) error {
84
- return c .topology .Connect (ctx )
88
+ err := c .topology .Connect (ctx )
89
+ if err != nil {
90
+ return err
91
+ }
92
+
93
+ return nil
94
+
85
95
}
86
96
87
97
// Disconnect closes sockets to the topology referenced by this Client. It will
@@ -93,9 +103,33 @@ func (c *Client) Connect(ctx context.Context) error {
93
103
// or write operations. If this method returns with no errors, all connections
94
104
// associated with this Client have been closed.
95
105
func (c * Client ) Disconnect (ctx context.Context ) error {
106
+ c .endSessions (ctx )
96
107
return c .topology .Disconnect (ctx )
97
108
}
98
109
110
+ // StartSession starts a new session.
111
+ func (c * Client ) StartSession () (* Session , error ) {
112
+ if c .topology .SessionPool == nil {
113
+ return nil , topology .ErrTopologyClosed
114
+ }
115
+
116
+ sess , err := session .NewClientSession (c .topology .SessionPool , c .id , session .Explicit )
117
+ if err != nil {
118
+ return nil , err
119
+ }
120
+
121
+ return & Session {Client : sess }, nil
122
+ }
123
+
124
+ func (c * Client ) endSessions (ctx context.Context ) {
125
+ cmd := command.EndSessions {
126
+ Clock : c .clock ,
127
+ SessionIDs : c .topology .SessionPool .IDSlice (),
128
+ }
129
+
130
+ _ , _ = dispatch .EndSessions (ctx , cmd , c .topology , description .ReadPrefSelector (readpref .PrimaryPreferred ()))
131
+ }
132
+
99
133
func newClient (cs connstring.ConnString , opts ... clientopt.Option ) (* Client , error ) {
100
134
clientOpt , err := clientopt .BundleClient (opts ... ).Unbundle (cs )
101
135
if err != nil {
@@ -108,15 +142,27 @@ func newClient(cs connstring.ConnString, opts ...clientopt.Option) (*Client, err
108
142
localThreshold : defaultLocalThreshold ,
109
143
}
110
144
145
+ uuid , err := uuid .New ()
146
+ if err != nil {
147
+ return nil , err
148
+ }
149
+ client .id = uuid
150
+
111
151
topts := append (
112
152
client .topologyOptions ,
113
153
topology .WithConnString (func (connstring.ConnString ) connstring.ConnString { return client .connString }),
154
+ topology .WithServerOptions (func (opts ... topology.ServerOption ) []topology.ServerOption {
155
+ return append (opts , topology .WithClock (func (clock * session.ClusterClock ) * session.ClusterClock {
156
+ return client .clock
157
+ }))
158
+ }),
114
159
)
115
160
topo , err := topology .New (topts ... )
116
161
if err != nil {
117
162
return nil , err
118
163
}
119
164
client .topology = topo
165
+ client .clock = & session.ClusterClock {}
120
166
121
167
if client .readConcern == nil {
122
168
client .readConcern = readConcernFromConnString (& client .connString )
@@ -212,6 +258,14 @@ func readPreferenceFromConnString(cs *connstring.ConnString) (*readpref.ReadPref
212
258
return rp , nil
213
259
}
214
260
261
+ // ValidSession returns an error if the session doesn't belong to the client
262
+ func (c * Client ) ValidSession (sess * session.Client ) error {
263
+ if sess != nil && ! uuid .Equal (sess .ClientID , c .id ) {
264
+ return ErrWrongClient
265
+ }
266
+ return nil
267
+ }
268
+
215
269
// Database returns a handle for a given database.
216
270
func (c * Client ) Database (name string , opts ... dbopt.Option ) * Database {
217
271
return newDatabase (c , name , opts ... )
@@ -222,39 +276,51 @@ func (c *Client) ConnectionString() string {
222
276
return c .connString .Original
223
277
}
224
278
225
- func (c * Client ) listDatabasesHelper (ctx context.Context , filter interface {},
226
- nameOnly bool ) (ListDatabasesResult , error ) {
227
-
228
- f , err := TransformDocument (filter )
279
+ // ListDatabases returns a ListDatabasesResult.
280
+ func (c * Client ) ListDatabases (ctx context.Context , filter interface {}, opts ... listdbopt.ListDatabases ) (ListDatabasesResult , error ) {
281
+ if ctx == nil {
282
+ ctx = context .Background ()
283
+ }
284
+ listDbOpts , sess , err := listdbopt .BundleListDatabases (opts ... ).Unbundle (true )
229
285
if err != nil {
230
286
return ListDatabasesResult {}, err
231
287
}
232
288
233
- opts := []option.ListDatabasesOptioner {}
289
+ err = c .ValidSession (sess )
290
+ if err != nil {
291
+ return ListDatabasesResult {}, err
292
+ }
234
293
235
- if nameOnly {
236
- opts = append (opts , option .OptNameOnly (nameOnly ))
294
+ f , err := TransformDocument (filter )
295
+ if err != nil {
296
+ return ListDatabasesResult {}, err
237
297
}
238
298
239
- cmd := command.ListDatabases {Filter : f , Opts : opts }
299
+ cmd := command.ListDatabases {
300
+ Filter : f ,
301
+ Opts : listDbOpts ,
302
+ Session : sess ,
303
+ Clock : c .clock ,
304
+ }
240
305
241
- // The spec indicates that we should not run the listDatabase command on a secondary in a
242
- // replica set.
243
- res , err := dispatch .ListDatabases (ctx , cmd , c .topology , description .ReadPrefSelector (readpref .Primary ()))
306
+ res , err := dispatch .ListDatabases (
307
+ ctx , cmd ,
308
+ c .topology ,
309
+ description .ReadPrefSelector (readpref .Primary ()),
310
+ c .id ,
311
+ c .topology .SessionPool ,
312
+ )
244
313
if err != nil {
245
314
return ListDatabasesResult {}, err
246
315
}
247
- return (ListDatabasesResult {}).fromResult (res ), nil
248
- }
249
316
250
- // ListDatabases returns a ListDatabasesResult.
251
- func (c * Client ) ListDatabases (ctx context.Context , filter interface {}) (ListDatabasesResult , error ) {
252
- return c .listDatabasesHelper (ctx , filter , false )
317
+ return (ListDatabasesResult {}).fromResult (res ), nil
253
318
}
254
319
255
320
// ListDatabaseNames returns a slice containing the names of all of the databases on the server.
256
- func (c * Client ) ListDatabaseNames (ctx context.Context , filter interface {}) ([]string , error ) {
257
- res , err := c .listDatabasesHelper (ctx , filter , true )
321
+ func (c * Client ) ListDatabaseNames (ctx context.Context , filter interface {}, opts ... listdbopt.ListDatabases ) ([]string , error ) {
322
+ opts = append (opts , listdbopt .NameOnly (true ))
323
+ res , err := c .ListDatabases (ctx , filter , opts ... )
258
324
if err != nil {
259
325
return nil , err
260
326
}
0 commit comments