@@ -116,7 +116,8 @@ defmodule Mongo.Session do
116
116
# * `implicit` true or false
117
117
# * `causal_consistency` true orfalse
118
118
# * `wire_version` current wire version to check if transactions are possible
119
- defstruct [ conn: nil , server_session: nil , causal_consistency: false , operation_time: nil , implicit: false , wire_version: 0 , state: :no_transaction , opts: [ ] ]
119
+ # * `recovery_token` tracked recovery token from response in a sharded transaction
120
+ defstruct [ conn: nil , recovery_token: nil , server_session: nil , causal_consistency: false , operation_time: nil , implicit: false , wire_version: 0 , state: :no_transaction , opts: [ ] ]
120
121
121
122
@ doc """
122
123
Start the generic state machine.
@@ -204,25 +205,35 @@ defmodule Mongo.Session do
204
205
"""
205
206
@ spec update_session ( Session . t , % { key: BSON.Timestamp . t } , keyword ( ) ) :: BSON . document
206
207
def update_session ( pid , doc , opts \\ [ ] )
207
- def update_session ( pid , % { "operationTime" => operationTime } = doc , opts ) do
208
+ def update_session ( pid , doc , opts ) do
208
209
case opts |> write_concern ( ) |> acknowledged? ( ) do
209
- true -> advance_operation_time ( pid , operationTime )
210
+ true -> advance_operation_time ( pid , doc [ " operationTime" ] )
210
211
false -> [ ]
211
212
end
212
- doc
213
- end
214
- def update_session ( _pid , doc , _opts ) do
213
+ update_recovery_token ( pid , doc [ "recoveryToken" ] )
215
214
doc
216
215
end
217
216
218
217
@ doc """
219
218
Advance the `operationTime` for causally consistent read commands
220
219
"""
221
220
@ spec advance_operation_time ( Session . t , BSON.Timestamp . t ) :: none ( )
221
+ def advance_operation_time ( _pid , nil ) do
222
+ end
222
223
def advance_operation_time ( pid , timestamp ) do
223
224
cast ( pid , { :advance_operation_time , timestamp } )
224
225
end
225
226
227
+ @ doc """
228
+ Update the `recoveryToken` after each response from mongos
229
+ """
230
+ @ spec update_recovery_token ( Session . t , BSON . document ) :: none ( )
231
+ def update_recovery_token ( _pid , nil ) do
232
+ end
233
+ def update_recovery_token ( pid , recovery_token ) do
234
+ cast ( pid , { :update_recovery_token , recovery_token } )
235
+ end
236
+
226
237
@ doc """
227
238
End implicit session. There is no need to call this function directly. It is called automatically.
228
239
"""
@@ -337,6 +348,7 @@ defmodule Mongo.Session do
337
348
server_session: server_session ,
338
349
implicit: ( type == :implicit ) ,
339
350
wire_version: wire_version ,
351
+ recovery_token: nil ,
340
352
causal_consistency: Keyword . get ( opts , :causal_consistency , false ) ,
341
353
state: :no_transaction ,
342
354
opts: opts }
@@ -378,7 +390,7 @@ defmodule Mongo.Session do
378
390
end
379
391
380
392
def handle_call_event ( :start_transaction , transaction , % Session { server_session: session } = data ) when transaction in [ :no_transaction , :transaction_aborted , :transaction_committed ] do
381
- { :next_state , :starting_transaction , % Session { data | server_session: ServerSession . next_txn_num ( session ) } , :ok }
393
+ { :next_state , :starting_transaction , % Session { data | recovery_token: nil , server_session: ServerSession . next_txn_num ( session ) } , :ok }
382
394
end
383
395
##
384
396
# bind session: only if wire_version >= 6, MongoDB 3.6.x and no transaction is running: only lsid is added
@@ -445,6 +457,9 @@ defmodule Mongo.Session do
445
457
def handle_call_event ( :server_session , _state , % Session { server_session: session_server , implicit: implicit } ) do
446
458
{ :keep_state_and_data , session_server , implicit }
447
459
end
460
+ def handle_cast_event ( { :update_recovery_token , recovery_token } , _state , % Session { } = data ) do
461
+ % Session { data | recovery_token: recovery_token }
462
+ end
448
463
def handle_cast_event ( { :advance_operation_time , timestamp } , _state , % Session { operation_time: nil } = data ) do
449
464
% Session { data | operation_time: timestamp }
450
465
end
@@ -457,7 +472,7 @@ defmodule Mongo.Session do
457
472
##
458
473
# Run the commit transaction command.
459
474
#
460
- defp run_commit_command ( % Session { conn: conn , server_session: % ServerSession { session_id: id , txn_num: txn_num } , opts: opts } ) do
475
+ defp run_commit_command ( % Session { conn: conn , recovery_token: recovery_token , server_session: % ServerSession { session_id: id , txn_num: txn_num } , opts: opts } ) do
461
476
462
477
Logger . debug ( "Running commit transaction" )
463
478
@@ -471,14 +486,21 @@ defmodule Mongo.Session do
471
486
txnNumber: % BSON.LongNumber { value: txn_num } ,
472
487
autocommit: false ,
473
488
writeConcern: write_concern ( opts ) ,
474
- maxTimeMS: Keyword . get ( opts , :max_commit_time_ms )
489
+ maxTimeMS: max_time_ms ( opts ) ,
490
+ recoveryToken: recovery_token
475
491
] |> filter_nils ( )
476
492
477
493
_doc = Mongo . exec_command ( conn , cmd , database: "admin" )
478
494
479
495
:ok
480
496
end
481
497
498
+ defp max_time_ms ( opts ) do
499
+ opts |> Keyword . get ( :max_commit_time_ms ) |> optional_int64 ( )
500
+ end
501
+ defp optional_int64 ( nil ) , do: nil
502
+ defp optional_int64 ( value ) , do: % BSON.LongNumber { value: value }
503
+
482
504
##
483
505
# Run the abort transaction command.
484
506
#
0 commit comments