33module Mongoid
44 module Clients
55
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.
97 module Sessions
108
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 )
5811 end
5912
6013 module ClassMethods
@@ -72,40 +25,83 @@ module ClassMethods
7225 # @param [ Hash ] options The session options. Please see the driver
7326 # documentation for the available session options.
7427 #
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- #
8328 # @raise [ Errors::InvalidSessionUse ] If an operation is attempted on a model using another
8429 # client from which the session was started or if sessions are nested.
8530 #
8631 # @return [ Object ] The result of calling the block.
8732 #
8833 # @yieldparam [ Mongo::Session ] The session being used for the block.
8934 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
9237 end
9338 session = persistence_context . client . start_session ( options )
94- Threaded . set_session ( session )
39+ Threaded . set_session ( session , client : persistence_context . client )
9540 yield ( session )
9641 rescue Mongo ::Error ::InvalidSession => ex
9742 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
9954 end
100- raise Mongoid ::Errors ::InvalidSessionUse . new ( :invalid_session_use )
10155 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
10399 end
104100
105101 private
106102
107103 def _session
108- Threaded . get_session
104+ Threaded . get_session ( client : persistence_context . client )
109105 end
110106 end
111107 end
0 commit comments