1
1
'use strict' ;
2
2
3
- const retrieveBSON = require ( './connection/utils' ) . retrieveBSON ,
4
- EventEmitter = require ( 'events' ) ,
5
- BSON = retrieveBSON ( ) ,
6
- Binary = BSON . Binary ,
7
- uuidV4 = require ( './utils' ) . uuidV4 ;
3
+ const retrieveBSON = require ( './connection/utils' ) . retrieveBSON ;
4
+ const EventEmitter = require ( 'events' ) ;
5
+ const BSON = retrieveBSON ( ) ;
6
+ const Binary = BSON . Binary ;
7
+ const uuidV4 = require ( './utils' ) . uuidV4 ;
8
+ const MongoError = require ( './error' ) . MongoError ;
8
9
9
- /**
10
- *
11
- */
10
+ function assertAlive ( session , callback ) {
11
+ if ( session . serverSession == null ) {
12
+ const error = new MongoError ( 'Cannot use a session that has ended' ) ;
13
+ if ( typeof callback === 'function' ) {
14
+ return callback ( error , null ) ;
15
+ }
16
+
17
+ throw error ;
18
+ }
19
+ } ;
20
+
21
+ /** A class representing a client session on the server */
12
22
class ClientSession extends EventEmitter {
23
+
24
+ /**
25
+ * Create a client session.
26
+ * WARNING: not meant to be instantiated directly
27
+ *
28
+ * @param {Topology } topology The current client's topology
29
+ * @param {ServerSessionPool } sessionPool The server session pool
30
+ * @param {Object } [options] Optional settings
31
+ * @param {Boolean } [options.causalConsistency] Whether causal consistency should be enabled on this session
32
+ * @param {Boolean } [options.autoStartTransaction=false] When enabled this session automatically starts a transaction with the provided defaultTransactionOptions.
33
+ * @param {Object } [options.defaultTransactionOptions] The default TransactionOptions to use for transactions started on this session.
34
+ */
13
35
constructor ( topology , sessionPool , options ) {
14
36
super ( ) ;
15
37
@@ -42,10 +64,20 @@ class ClientSession extends EventEmitter {
42
64
43
65
this . explicit = ! ! options . explicit ;
44
66
this . owner = options . owner ;
67
+ this . transactionOptions = null ;
68
+ this . defaultTransactionOptions = options . defaultTransactionOptions || { } ;
69
+
70
+ if ( options . autoStartTransaction ) {
71
+ this . startTransaction ( ) ;
72
+ }
45
73
}
46
74
47
75
/**
76
+ * Ends this session on the server
48
77
*
78
+ * @param {Object } [options] Optional settings
79
+ * @param {Boolean } [options.skipCommand] Skip sending the actual endSessions command to the server
80
+ * @param {Function } [callback] Optional callback for completion of this operation
49
81
*/
50
82
endSession ( options , callback ) {
51
83
if ( typeof options === 'function' ) ( callback = options ) , ( options = { } ) ;
@@ -56,6 +88,10 @@ class ClientSession extends EventEmitter {
56
88
return ;
57
89
}
58
90
91
+ if ( this . serverSession && this . inTransaction ( ) ) {
92
+ this . abortTransaction ( ) ; // pass in callback?
93
+ }
94
+
59
95
if ( ! options . skipCommand ) {
60
96
// send the `endSessions` command
61
97
this . topology . endSessions ( this . id ) ;
@@ -98,6 +134,98 @@ class ClientSession extends EventEmitter {
98
134
99
135
return this . id . id . buffer . equals ( session . id . id . buffer ) ;
100
136
}
137
+
138
+ /**
139
+ * @returns whether this session is current in a transaction or not
140
+ */
141
+ inTransaction ( ) {
142
+ return this . transactionOptions != null ;
143
+ }
144
+
145
+ /**
146
+ * Starts a new transaction with the given options.
147
+ *
148
+ * @param {Object } options Optional settings
149
+ * @param {ReadConcern } [options.readConcern] The readConcern to use for this transaction
150
+ * @param {WriteConcern } [options.writeConcern] The writeConcern to use for this transaction
151
+ */
152
+ startTransaction ( options ) {
153
+ assertAlive ( this ) ;
154
+ if ( this . inTransaction ( ) ) {
155
+ throw new MongoError ( 'Transaction already started' ) ;
156
+ }
157
+
158
+ // increment txnNumber and reset stmtId to zero.
159
+ this . serverSession . txnNumber += 1 ;
160
+ this . serverSession . stmtId = 0 ;
161
+
162
+ // set transaction options, we will use this to determine if we are in a transaction
163
+ this . transactionOptions = options || this . defaultTransactionOptions ;
164
+ }
165
+
166
+ /**
167
+ * Commits the currently active transaction in this session.
168
+ *
169
+ * @param {Function } [callback] optional callback for completion of this operation
170
+ * @return {Promise } A promise is returned if no callback is provided
171
+ */
172
+ commitTransaction ( callback ) {
173
+ if ( typeof callback === 'function' ) {
174
+ endTransaction ( this , 'commitTransaction' , callback ) ;
175
+ return ;
176
+ }
177
+
178
+ return new Promise ( ( resolve , reject ) => {
179
+ endTransaction ( this , 'commitTransaction' , ( err , reply ) => err ? reject ( err ) : resolve ( reply ) ) ;
180
+ } ) ;
181
+ }
182
+
183
+ /**
184
+ * Aborts the currently active transaction in this session.
185
+ *
186
+ * @param {Function } [callback] optional callback for completion of this operation
187
+ * @return {Promise } A promise is returned if no callback is provided
188
+ */
189
+ abortTransaction ( callback ) {
190
+ if ( typeof callback === 'function' ) {
191
+ endTransaction ( this , 'abortTransaction' , callback ) ;
192
+ return ;
193
+ }
194
+
195
+ return new Promise ( ( resolve , reject ) => {
196
+ endTransaction ( this , 'abortTransaction' , ( err , reply ) => err ? reject ( err ) : resolve ( reply ) ) ;
197
+ } ) ;
198
+
199
+ }
200
+ }
201
+
202
+ function endTransaction ( clientSession , commandName , callback ) {
203
+ assertAlive ( clientSession , callback ) ;
204
+
205
+ if ( ! clientSession . inTransaction ( ) ) {
206
+ callback ( new MongoError ( 'No transaction started' ) ) ;
207
+ return ;
208
+ }
209
+
210
+ if ( clientSession . serverSession . stmtId === 0 ) {
211
+ // The server transaction was never started.
212
+ callback ( null , null ) ;
213
+ return ;
214
+ }
215
+
216
+ // send the command
217
+ clientSession . topology . command ( 'admin.$cmd' , { [ commandName ] : 1 } , {
218
+ writeConcern : clientSession . transactionOptions . writeConcern
219
+ } , ( err , reply ) => {
220
+ // reset internal transaction state
221
+ if ( clientSession . options . autoStartTransaction ) {
222
+ clientSession . startTransaction ( ) ;
223
+ } else {
224
+ clientSession . transactionOptions = null ;
225
+ }
226
+
227
+ callback ( err , reply ) ;
228
+ } ) ;
101
229
}
102
230
103
231
Object . defineProperty ( ClientSession . prototype , 'id' , {
0 commit comments