@@ -15,6 +15,7 @@ defmodule Mongo.Session do
15
15
alias Mongo.Session.ServerSession
16
16
alias Mongo.Session
17
17
alias Mongo.Topology
18
+ alias BSON.Timestamp
18
19
19
20
require Logger
20
21
@@ -26,7 +27,7 @@ defmodule Mongo.Session do
26
27
# * `server_session` the server_session data
27
28
# * `opts` options
28
29
# * `implicit` true or false
29
- defstruct [ conn: nil , server_session: nil , implicit: false , wire_version: 0 , opts: [ ] ]
30
+ defstruct [ conn: nil , server_session: nil , causal_consistency: false , operation_time: nil , implicit: false , wire_version: 0 , opts: [ ] ]
30
31
31
32
@ impl true
32
33
def callback_mode ( ) do
@@ -99,6 +100,30 @@ defmodule Mongo.Session do
99
100
:gen_statem . call ( pid , { :bind_session , cmd } )
100
101
end
101
102
103
+ @ doc """
104
+ Update the `operationTime` for causally consistent read commands
105
+ """
106
+ def update_session ( pid , % { "operationTime" => operationTime } = doc , opts ) do
107
+ case opts |> write_concern ( ) |> acknowledged? ( ) do
108
+ true -> advance_operation_time ( pid , operationTime )
109
+ false -> [ ]
110
+ end
111
+ doc
112
+ end
113
+ def update_session ( _pid , doc , _opts ) do
114
+ doc
115
+ end
116
+
117
+ @ doc """
118
+ Advance the `operationTime` for causally consistent read commands
119
+ """
120
+ def advance_operation_time ( pid , timestamp ) do
121
+ :gen_statem . cast ( pid , { :advance_operation_time , timestamp } )
122
+ end
123
+
124
+ @ doc """
125
+ End implicit session
126
+ """
102
127
def end_implict_session ( topology_pid , session ) do
103
128
with { :ok , session_server } <- :gen_statem . call ( session , { :end_implicit_session } ) do
104
129
Topology . checkin_session ( topology_pid , session_server )
@@ -108,12 +133,18 @@ defmodule Mongo.Session do
108
133
end
109
134
end
110
135
136
+ @ doc """
137
+ End explicit session
138
+ """
111
139
def end_session ( topology_pid , session ) do
112
140
with { :ok , session_server } <- :gen_statem . call ( session , { :end_session } ) do
113
141
Topology . checkin_session ( topology_pid , session_server )
114
142
end
115
143
end
116
144
145
+ @ doc """
146
+ Convient function for running multiple write commands in a transaction
147
+ """
117
148
def with_transaction ( topology_pid , fun , opts \\ [ ] ) do
118
149
119
150
with { :ok , session } <- Session . start_session ( topology_pid , :write , opts ) ,
@@ -127,7 +158,6 @@ defmodule Mongo.Session do
127
158
error ->
128
159
abort_transaction ( session )
129
160
end_session ( topology_pid , session )
130
- ## todo rerun
131
161
error
132
162
end
133
163
@@ -164,6 +194,7 @@ defmodule Mongo.Session do
164
194
server_session: server_session ,
165
195
implicit: ( type == :implicit ) ,
166
196
wire_version: wire_version ,
197
+ causal_consistency: Keyword . get ( opts , :causal_consistency , false ) ,
167
198
opts: opts }
168
199
{ :ok , :no_transaction , data }
169
200
end
@@ -184,19 +215,21 @@ defmodule Mongo.Session do
184
215
transaction ,
185
216
% Session { conn: conn ,
186
217
wire_version: wire_version ,
187
- server_session: % ServerSession { session_id: id } } ) when wire_version >= 6 and transaction in [ :no_transaction , :transaction_aborted , :transaction_committed ] do
188
- { :keep_state_and_data , { :reply , from , { :ok , conn , Keyword . merge ( cmd , lsid: % { id: id } ) } } }
218
+ server_session: % ServerSession { session_id: id } } = data ) when wire_version >= 6 and transaction in [ :no_transaction , :transaction_aborted , :transaction_committed ] do
219
+
220
+ cmd = Keyword . merge ( cmd , lsid: % { id: id } , readConcern: read_concern ( data , Keyword . get ( cmd , :readConcern ) ) ) |> filter_nils ( )
221
+ { :keep_state_and_data , { :reply , from , { :ok , conn , cmd } } }
189
222
end
190
223
191
224
def handle_event ( { :call , from } ,
192
225
{ :bind_session , cmd } ,
193
226
:starting_transaction ,
194
227
% Session { conn: conn ,
195
228
server_session: % ServerSession { session_id: id , txn_num: txn_num } ,
196
- wire_version: wire_version ,
197
- opts: opts } = data ) when wire_version >= 6 do
229
+ wire_version: wire_version } = data ) when wire_version >= 6 do
230
+
198
231
result = Keyword . merge ( cmd ,
199
- readConcern: Keyword . get ( opts , :read_concern ) ,
232
+ readConcern: read_concern ( data , Keyword . get ( cmd , :readConcern ) ) ,
200
233
lsid: % { id: id } ,
201
234
txnNumber: % BSON.LongNumber { value: txn_num } ,
202
235
startTransaction: true ,
@@ -253,6 +286,15 @@ defmodule Mongo.Session do
253
286
def handle_event ( { :call , from } , { :server_session } , _state , % Session { server_session: session_server , implicit: implicit } ) do
254
287
{ :keep_state_and_data , { :reply , from , { :ok , session_server , implicit } } }
255
288
end
289
+ def handle_event ( :cast , { :advance_operation_time , timestamp } , _state , % Session { operation_time: nil } = data ) do
290
+ { :keep_state , % Session { data | operation_time: timestamp } }
291
+ end
292
+ def handle_event ( :cast , { :advance_operation_time , timestamp } , _state , % Session { operation_time: time } = data ) do
293
+ case Timestamp . is_after ( timestamp , time ) do
294
+ true -> { :keep_state , % Session { data | operation_time: timestamp } }
295
+ false -> :keep_state_and_data
296
+ end
297
+ end
256
298
257
299
@ impl true
258
300
def terminate ( reason , state , data ) when state in [ :transaction_in_progress ] do
@@ -312,4 +354,23 @@ defmodule Mongo.Session do
312
354
end
313
355
314
356
357
+ ##
358
+ # create the readConcern options
359
+ #
360
+ defp read_concern ( % Session { causal_consistency: false } , read_concern ) do
361
+ read_concern
362
+ end
363
+ defp read_concern ( % Session { causal_consistency: true , operation_time: nil } , read_concern ) do
364
+ read_concern
365
+ end
366
+ defp read_concern ( % Session { causal_consistency: true , operation_time: time } , nil ) do
367
+ [ afterClusterTime: time ]
368
+ end
369
+ defp read_concern ( % Session { causal_consistency: true , operation_time: time } , read_concern ) when is_map ( read_concern ) do
370
+ Map . put ( read_concern , :afterClusterTime , time )
371
+ end
372
+ defp read_concern ( % Session { causal_consistency: true , operation_time: time } , read_concern ) when is_list ( read_concern ) do
373
+ read_concern ++ [ afterClusterTime: time ]
374
+ end
375
+
315
376
end
0 commit comments