3
3
module Mongoid
4
4
module Clients
5
5
6
- # Encapsulates behavior for getting a session from the client of a model class or instance,
7
- # setting the session on the current thread, and yielding to a block.
8
- # The session will be closed after the block completes or raises an error.
6
+ # Encapsulates behavior for using sessions and transactions.
9
7
module Sessions
10
8
11
- # Execute a block within the context of a session.
12
- #
13
- # @example Execute some operations in the context of a session.
14
- # band.with_session(causal_consistency: true) do
15
- # band.records << Record.create
16
- # band.name = 'FKA Twigs'
17
- # band.save
18
- # band.reload
19
- # end
20
- #
21
- # @param [ Hash ] options The session options. Please see the driver
22
- # documentation for the available session options.
23
- #
24
- # @note You cannot do any operations in the block using models or objects
25
- # that use a different client; the block will execute all operations
26
- # in the context of the implicit session and operations on any models using
27
- # another client will fail. For example, if you set a client using store_in on a
28
- # particular model and execute an operation on it in the session context block,
29
- # that operation can't use the block's session and an error will be raised.
30
- # An error will also be raised if sessions are nested.
31
- #
32
- # @raise [ Errors::InvalidSessionUse ] If an operation is attempted on a model using another
33
- # client from which the session was started or if sessions are nested.
34
- #
35
- # @return [ Object ] The result of calling the block.
36
- #
37
- # @yieldparam [ Mongo::Session ] The session being used for the block.
38
- def with_session ( options = { } )
39
- if Threaded . get_session
40
- raise Mongoid ::Errors ::InvalidSessionUse . new ( :invalid_session_nesting )
41
- end
42
- session = persistence_context . client . start_session ( options )
43
- Threaded . set_session ( session )
44
- yield ( session )
45
- rescue Mongo ::Error ::InvalidSession => ex
46
- if Mongo ::Error ::SessionsNotSupported === ex
47
- raise Mongoid ::Errors ::InvalidSessionUse . new ( :sessions_not_supported )
48
- end
49
- raise Mongoid ::Errors ::InvalidSessionUse . new ( :invalid_session_use )
50
- ensure
51
- Threaded . clear_session
52
- end
53
-
54
- private
55
-
56
- def _session
57
- Threaded . get_session
9
+ def self . included ( base )
10
+ base . include ( ClassMethods )
58
11
end
59
12
60
13
module ClassMethods
@@ -72,40 +25,83 @@ module ClassMethods
72
25
# @param [ Hash ] options The session options. Please see the driver
73
26
# documentation for the available session options.
74
27
#
75
- # @note You cannot do any operations in the block using models or objects
76
- # that use a different client; the block will execute all operations
77
- # in the context of the implicit session and operations on any models using
78
- # another client will fail. For example, if you set a client using store_in on a
79
- # particular model and execute an operation on it in the session context block,
80
- # that operation can't use the block's session and an error will be raised.
81
- # You also cannot nest sessions.
82
- #
83
28
# @raise [ Errors::InvalidSessionUse ] If an operation is attempted on a model using another
84
29
# client from which the session was started or if sessions are nested.
85
30
#
86
31
# @return [ Object ] The result of calling the block.
87
32
#
88
33
# @yieldparam [ Mongo::Session ] The session being used for the block.
89
34
def with_session ( options = { } )
90
- if Threaded . get_session
91
- raise Mongoid ::Errors ::InvalidSessionUse . new ( :invalid_session_nesting )
35
+ if Threaded . get_session ( client : persistence_context . client )
36
+ raise Mongoid ::Errors ::InvalidSessionNesting . new
92
37
end
93
38
session = persistence_context . client . start_session ( options )
94
- Threaded . set_session ( session )
39
+ Threaded . set_session ( session , client : persistence_context . client )
95
40
yield ( session )
96
41
rescue Mongo ::Error ::InvalidSession => ex
97
42
if Mongo ::Error ::SessionsNotSupported === ex
98
- raise Mongoid ::Errors ::InvalidSessionUse . new ( :sessions_not_supported )
43
+ raise Mongoid ::Errors ::SessionsNotSupported . new
44
+ else
45
+ raise ex
46
+ end
47
+ rescue Mongo ::Error ::OperationFailure => ex
48
+ if ( ex . code == 40415 && ex . server_message =~ /startTransaction/ ) ||
49
+ ( ex . code == 20 && ex . server_message =~ /Transaction/ )
50
+ then
51
+ raise Mongoid ::Errors ::TransactionsNotSupported . new
52
+ else
53
+ raise ex
99
54
end
100
- raise Mongoid ::Errors ::InvalidSessionUse . new ( :invalid_session_use )
101
55
ensure
102
- Threaded . clear_session
56
+ Threaded . clear_session ( client : persistence_context . client )
57
+ end
58
+
59
+ # Executes a block within the context of a transaction.
60
+ #
61
+ # If the block does not raise an error, the transaction is committed.
62
+ # If an error is raised, the transaction is aborted. The error is passed on
63
+ # except for the `Mongoid::Errors::Rollback`. This error is not passed on,
64
+ # so you can raise is if you want to deliberately rollback the transaction.
65
+ #
66
+ # @param [ Hash ] options The transaction options. Please see the driver
67
+ # documentation for the available session options.
68
+ # @param [ Hash ] session_options The session options. A MongoDB
69
+ # transaction must be started inside a session, therefore a session will
70
+ # be started. Please see the driver documentation for the available session options.
71
+ #
72
+ # @raise [ Mongoid::Errors::InvalidTransactionNesting ] If the transaction is
73
+ # opened on a client that already has an open transaction.
74
+ # @raise [ Mongoid::Errors::TransactionsNotSupported ] If MongoDB deployment
75
+ # the client is connected to does not support transactions.
76
+ # @raise [ Mongoid::Errors::TransactionError ] If there is an error raised
77
+ # by MongoDB deployment or MongoDB driver.
78
+ #
79
+ # @yield Provided block will be executed inside a transaction.
80
+ def transaction ( options = { } , session_options : { } )
81
+ with_session ( session_options ) do |session |
82
+ begin
83
+ session . start_transaction ( options )
84
+ yield
85
+ session . commit_transaction
86
+ rescue Mongoid ::Errors ::Rollback
87
+ session . abort_transaction
88
+ rescue Mongoid ::Errors ::InvalidSessionNesting
89
+ # Session should be ended here.
90
+ raise Mongoid ::Errors ::InvalidTransactionNesting . new
91
+ rescue Mongo ::Error ::InvalidSession , Mongo ::Error ::InvalidTransactionOperation => e
92
+ session . abort_transaction
93
+ raise Mongoid ::Errors ::TransactionError ( e )
94
+ rescue StandardError => e
95
+ session . abort_transaction
96
+ raise e
97
+ end
98
+ end
103
99
end
104
100
105
101
private
106
102
107
103
def _session
108
- Threaded . get_session
104
+ Threaded . get_session ( client : persistence_context . client )
109
105
end
110
106
end
111
107
end
0 commit comments