1
1
defmodule Mongo.Session do
2
- @ enforce_keys [ :id , :pid ]
2
+ @ enforce_keys [ :session , :pid ]
3
3
defstruct @ enforce_keys ++
4
4
[
5
5
:ref ,
@@ -9,11 +9,7 @@ defmodule Mongo.Session do
9
9
operation_time: nil ,
10
10
causal_consistency: true ,
11
11
retry_writes: false ,
12
- txn: % {
13
- seq: 0 ,
14
- read_concern: nil ,
15
- write_concern: nil
16
- }
12
+ active_txn: nil
17
13
]
18
14
19
15
@ opaque session :: pid ( )
@@ -24,8 +20,8 @@ defmodule Mongo.Session do
24
20
defmodule Supervisor do
25
21
@ moduledoc false
26
22
27
- def start_child ( conn , id , opts ) do
28
- DynamicSupervisor . start_child ( __MODULE__ , { Mongo.Session , { conn , id , opts , self ( ) } } )
23
+ def start_child ( conn , session , opts , parent ) do
24
+ DynamicSupervisor . start_child ( __MODULE__ , { Mongo.Session , { conn , session , opts , parent } } )
29
25
end
30
26
31
27
def child_spec ( _ ) do
@@ -66,9 +62,9 @@ defmodule Mongo.Session do
66
62
"""
67
63
@ spec end_session ( session ( ) ) :: :ok
68
64
def end_session ( pid ) do
69
- Mongo.SessionPool . checkin ( pid )
70
-
71
- :ok
65
+ with { :ok , id , txn } <- :gen_statem . call ( pid , :end_session ) do
66
+ Mongo.SessionPool . checkin ( id , txn )
67
+ end
72
68
end
73
69
74
70
@ doc """
@@ -105,25 +101,6 @@ defmodule Mongo.Session do
105
101
:gen_statem . call ( pid , { :advance_operation_time , timestamp } )
106
102
end
107
103
108
- @ doc false
109
- def set_options ( pid , opts ) do
110
- causal_consistency = Keyword . get ( opts , :causal_consistency , true )
111
- read_concern = Keyword . get ( opts , :read_concern , % { } )
112
- read_preference = Keyword . get ( opts , :read_preference )
113
- retry_writes = Keyword . get ( opts , :retry_writes , true )
114
- write_concern = Keyword . get ( opts , :write_concern )
115
-
116
- state = % {
117
- causal_consistency: causal_consistency ,
118
- read_concern: read_concern ,
119
- read_preference: read_preference ,
120
- retry_writes: retry_writes ,
121
- write_concern: write_concern
122
- }
123
-
124
- :gen_statem . call ( pid , { :set_options , state } )
125
- end
126
-
127
104
@ doc false
128
105
def update_session ( doc , nil ) , do: doc
129
106
@@ -151,15 +128,15 @@ defmodule Mongo.Session do
151
128
@ outside_txn @ states -- @ in_txn
152
129
153
130
@ doc false
154
- def child_spec ( { topology_pid , id , opts , parent } ) do
131
+ def child_spec ( { topology_pid , session , opts , parent } ) do
155
132
causal_consistency = Keyword . get ( opts , :causal_consistency , true )
156
133
read_concern = Keyword . get ( opts , :read_concern , % { } )
157
134
read_preference = Keyword . get ( opts , :read_preference )
158
135
retry_writes = Keyword . get ( opts , :retry_writes , true )
159
136
write_concern = Keyword . get ( opts , :write_concern )
160
137
161
138
state = % __MODULE__ {
162
- id: id ,
139
+ session: session ,
163
140
pid: topology_pid ,
164
141
causal_consistency: causal_consistency ,
165
142
read_concern: read_concern ,
@@ -200,25 +177,27 @@ defmodule Mongo.Session do
200
177
end
201
178
202
179
# Start new transaction if there isn't one already.
203
- def handle_event ( { :call , from } , { :start_transaction , opts } , state , % { txn: % { seq: seq } } = data )
180
+ def handle_event ( { :call , from } , { :start_transaction , opts } , state , % { session: session } = data )
204
181
when state in @ outside_txn do
205
182
write_concern = Keyword . get ( opts , :write_concern , data . write_concern )
206
183
read_concern = Keyword . get ( opts , :read_concern , data . read_concern )
207
184
208
185
txn = % {
209
- seq: seq + 1 ,
210
186
write_concern: write_concern ,
211
187
read_concern: read_concern
212
188
}
213
189
214
- { :next_state , :transaction_started , struct ( data , txn: txn ) , { :reply , from , :ok } }
190
+ session = Map . update! ( session , :txn , & & 1 + 1 )
191
+
192
+ { :next_state , :transaction_started , struct ( data , session: session , active_txn: txn ) ,
193
+ { :reply , from , :ok } }
215
194
end
216
195
217
196
# Add session information to the query metadata.
218
197
def handle_event ( { :call , from } , { :add_session , query } , :transaction_started , data ) do
219
198
% {
220
- txn : % {
221
- seq: seq ,
199
+ session : % { txn: seq , id: id } = session ,
200
+ active_txn: % {
222
201
read_concern: read_concern ,
223
202
write_concern: write_concern
224
203
}
@@ -227,33 +206,39 @@ defmodule Mongo.Session do
227
206
new_query =
228
207
query
229
208
|> Keyword . new ( )
230
- |> add_option ( :lsid , data . id )
209
+ |> add_option ( :lsid , % { id: id } )
231
210
|> add_option ( :txnNumber , { :long , seq } )
232
211
|> add_option ( :startTransaction , true )
233
212
|> add_option ( :autocommit , false )
234
213
|> add_option ( :writeConcern , write_concern )
235
214
|> add_option ( :readConcern , read_concern )
236
215
|> set_read_concern ( data . operation_time , data . causal_consistency )
237
216
238
- { :next_state , :in_transaction , data , { :reply , from , { :ok , new_query } } }
217
+ session = Map . put ( session , :last_use , :erlang . monotonic_time ( ) )
218
+
219
+ { :next_state , :in_transaction , struct ( data , session: session ) ,
220
+ { :reply , from , { :ok , new_query } } }
239
221
end
240
222
241
223
def handle_event ( { :call , from } , { :add_session , query } , :in_transaction , data ) do
242
224
new_query =
243
225
query
244
226
|> Keyword . new ( )
245
227
|> Keyword . merge (
246
- lsid: data . id ,
247
- txnNumber: { :long , data . txn } ,
228
+ lsid: % { id: data . session . id } ,
229
+ txnNumber: { :long , data . session . txn } ,
248
230
autocommit: false
249
231
)
250
232
233
+ session = Map . put ( data . session , :last_use , :erlang . monotonic_time ( ) )
234
+ data = struct ( data , session: session )
235
+
251
236
case Keyword . get ( new_query , :read_preference , % { mode: :primary } ) do
252
237
% { mode: :primary } ->
253
- { :keep_state_and_data , { :reply , from , { :ok , new_query } } }
238
+ { :keep_state , data , { :reply , from , { :ok , new_query } } }
254
239
255
240
% { mode: mode } ->
256
- { :keep_state_and_data ,
241
+ { :keep_state , data ,
257
242
{ :reply , from ,
258
243
{ :error ,
259
244
Mongo.Error . exception ( message: "Read preference must be primary, not: #{ mode } " ) } } }
@@ -267,7 +252,7 @@ defmodule Mongo.Session do
267
252
new_query =
268
253
query
269
254
|> Keyword . new ( )
270
- |> add_option ( :lsid , data . id )
255
+ |> add_option ( :lsid , data . session . id )
271
256
|> set_read_concern ( data . operation_time , data . causal_consistency )
272
257
273
258
{ :next_state , :no_transaction , data , { :reply , from , { :ok , new_query } } }
@@ -303,8 +288,13 @@ defmodule Mongo.Session do
303
288
304
289
# Finish session by ending process (for further "closing" see `terminate/3`
305
290
# handler.
306
- def handle_event ( { :call , from } , :end_session , _state , _data ) do
307
- { :stop_and_reply , :normal , { :reply , from , :ok } }
291
+ def handle_event ( { :call , from } , :end_session , state , % { session: session } = data ) do
292
+ _ =
293
+ if state == :in_transaction do
294
+ try_run_txn_command ( data , :abortTransaction )
295
+ end
296
+
297
+ { :stop_and_reply , :normal , [ { :reply , from , { :ok , session } } ] }
308
298
end
309
299
310
300
def handle_event ( { :call , from } , { :advance_operation_time , timestamp } , _state , data ) do
@@ -318,10 +308,6 @@ defmodule Mongo.Session do
318
308
end
319
309
end
320
310
321
- def handle_event ( { :call , from } , { :set_options , opts } , _state , data ) do
322
- { :keep_state , struct ( data , opts ) , { :reply , from , :ok } }
323
- end
324
-
325
311
# If parent process died before session then stop process and handle aborting
326
312
# sessions in `terminate/3` handler.
327
313
def handle_event ( :info , { :DOWN , ref , :process , _pid , _reason } , _state , % { ref: ref } ) do
@@ -339,12 +325,13 @@ defmodule Mongo.Session do
339
325
@ impl :gen_statem
340
326
# Abort all pending transactions if there any and end session itself.
341
327
def terminate ( _reason , state , % { pid: pid } = data ) do
342
- if state == :in_transaction do
343
- _ = try_run_txn_command ( data , :abortTransaction )
344
- end
328
+ _ =
329
+ if state == :in_transaction do
330
+ try_run_txn_command ( data , :abortTransaction )
331
+ end
345
332
346
333
query = % {
347
- endSessions: [ data . id ]
334
+ endSessions: [ data . session . id ]
348
335
}
349
336
350
337
with { :ok , conn , _ , _ } <- Mongo . select_server ( pid , :write , [ ] ) ,
@@ -380,9 +367,9 @@ defmodule Mongo.Session do
380
367
query =
381
368
[
382
369
{ command , 1 } ,
383
- lsid: state . id ,
370
+ lsid: % { id: state . session . id } ,
384
371
autocommit: false ,
385
- txnNumber: { :long , state . txn . seq }
372
+ txnNumber: { :long , state . session . txn }
386
373
]
387
374
|> add_option ( :writeConcern , state . write_concern )
388
375
0 commit comments