mongo 2.9.2 → 2.10.0.rc0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/mongo.rb +1 -0
- data/lib/mongo/auth/user/view.rb +4 -4
- data/lib/mongo/bulk_write.rb +14 -8
- data/lib/mongo/bulk_write/result.rb +1 -1
- data/lib/mongo/bulk_write/result_combiner.rb +2 -2
- data/lib/mongo/bulk_write/transformable.rb +17 -9
- data/lib/mongo/client.rb +107 -16
- data/lib/mongo/cluster.rb +47 -25
- data/lib/mongo/cluster/topology/replica_set_no_primary.rb +1 -1
- data/lib/mongo/cluster_time.rb +139 -0
- data/lib/mongo/collection.rb +84 -25
- data/lib/mongo/collection/view.rb +7 -3
- data/lib/mongo/collection/view/aggregation.rb +4 -4
- data/lib/mongo/collection/view/builder/aggregation.rb +31 -6
- data/lib/mongo/collection/view/builder/find_command.rb +4 -1
- data/lib/mongo/collection/view/builder/map_reduce.rb +4 -1
- data/lib/mongo/collection/view/change_stream.rb +54 -66
- data/lib/mongo/collection/view/iterable.rb +2 -2
- data/lib/mongo/collection/view/map_reduce.rb +6 -4
- data/lib/mongo/collection/view/readable.rb +36 -16
- data/lib/mongo/collection/view/writable.rb +68 -22
- data/lib/mongo/cursor.rb +87 -20
- data/lib/mongo/database.rb +47 -43
- data/lib/mongo/database/view.rb +54 -11
- data/lib/mongo/error.rb +13 -4
- data/lib/mongo/error/invalid_write_concern.rb +2 -2
- data/lib/mongo/error/operation_failure.rb +65 -11
- data/lib/mongo/error/parser.rb +41 -8
- data/lib/mongo/grid/fs_bucket.rb +26 -6
- data/lib/mongo/grid/stream/read.rb +9 -2
- data/lib/mongo/grid/stream/write.rb +21 -5
- data/lib/mongo/index/view.rb +3 -3
- data/lib/mongo/lint.rb +10 -3
- data/lib/mongo/operation.rb +2 -0
- data/lib/mongo/operation/aggregate/result.rb +19 -6
- data/lib/mongo/operation/collections_info.rb +1 -1
- data/lib/mongo/operation/get_more/result.rb +9 -0
- data/lib/mongo/operation/list_collections/command.rb +1 -3
- data/lib/mongo/operation/list_collections/op_msg.rb +1 -2
- data/lib/mongo/operation/parallel_scan/command.rb +4 -1
- data/lib/mongo/operation/parallel_scan/op_msg.rb +4 -1
- data/lib/mongo/operation/result.rb +27 -4
- data/lib/mongo/operation/shared/executable.rb +19 -5
- data/lib/mongo/operation/shared/executable_no_validate.rb +1 -2
- data/lib/mongo/operation/shared/executable_transaction_label.rb +0 -9
- data/lib/mongo/operation/shared/polymorphic_result.rb +9 -1
- data/lib/mongo/operation/shared/result/aggregatable.rb +2 -2
- data/lib/mongo/operation/shared/sessions_supported.rb +42 -32
- data/lib/mongo/operation/shared/specifiable.rb +40 -0
- data/lib/mongo/operation/shared/unpinnable.rb +39 -0
- data/lib/mongo/operation/shared/write.rb +1 -1
- data/lib/mongo/protocol/update.rb +6 -2
- data/lib/mongo/retryable.rb +79 -39
- data/lib/mongo/server/connection.rb +10 -3
- data/lib/mongo/server/description.rb +25 -1
- data/lib/mongo/server/monitor/connection.rb +1 -1
- data/lib/mongo/server_selector.rb +10 -0
- data/lib/mongo/server_selector/selectable.rb +172 -32
- data/lib/mongo/session.rb +654 -581
- data/lib/mongo/session/session_pool.rb +1 -1
- data/lib/mongo/socket.rb +7 -28
- data/lib/mongo/socket/ssl.rb +26 -1
- data/lib/mongo/socket/tcp.rb +3 -0
- data/lib/mongo/socket/unix.rb +3 -0
- data/lib/mongo/uri.rb +112 -265
- data/lib/mongo/uri/srv_protocol.rb +4 -1
- data/lib/mongo/version.rb +1 -1
- data/lib/mongo/write_concern.rb +10 -29
- data/lib/mongo/write_concern/acknowledged.rb +12 -0
- data/lib/mongo/write_concern/base.rb +17 -13
- data/lib/mongo/write_concern/unacknowledged.rb +12 -0
- data/spec/atlas/atlas_connectivity_spec.rb +7 -37
- data/spec/atlas/operations_spec.rb +25 -0
- data/spec/integration/change_stream_examples_spec.rb +45 -31
- data/spec/integration/change_stream_spec.rb +305 -5
- data/spec/integration/client_spec.rb +44 -0
- data/spec/integration/command_monitoring_spec.rb +1 -0
- data/spec/integration/command_spec.rb +7 -1
- data/spec/integration/mmapv1_spec.rb +28 -0
- data/spec/integration/mongos_pinning_spec.rb +34 -0
- data/spec/integration/operation_failure_code_spec.rb +2 -2
- data/spec/integration/{read_concern.rb → read_concern_spec.rb} +7 -1
- data/spec/integration/read_preference_spec.rb +485 -0
- data/spec/integration/retryable_writes_spec.rb +8 -19
- data/spec/integration/sdam_error_handling_spec.rb +1 -1
- data/spec/integration/sdam_events_spec.rb +2 -2
- data/spec/integration/server_description_spec.rb +14 -17
- data/spec/integration/server_selector_spec.rb +7 -3
- data/spec/integration/server_spec.rb +48 -0
- data/spec/integration/ssl_uri_options_spec.rb +1 -1
- data/spec/integration/step_down_spec.rb +10 -4
- data/spec/integration/transactions_examples_spec.rb +11 -10
- data/spec/lite_spec_helper.rb +19 -16
- data/spec/mongo/auth/scram/negotiation_spec.rb +11 -8
- data/spec/mongo/bulk_write/ordered_combiner_spec.rb +6 -6
- data/spec/mongo/bulk_write/unordered_combiner_spec.rb +4 -4
- data/spec/mongo/bulk_write_spec.rb +12 -2
- data/spec/mongo/client_construction_spec.rb +160 -8
- data/spec/mongo/client_spec.rb +5 -4
- data/spec/mongo/cluster_spec.rb +6 -6
- data/spec/mongo/cluster_time_spec.rb +148 -0
- data/spec/mongo/collection/view/aggregation_spec.rb +34 -15
- data/spec/mongo/collection/view/change_stream_spec.rb +62 -3
- data/spec/mongo/collection/view/map_reduce_spec.rb +7 -5
- data/spec/mongo/collection/view/readable_spec.rb +4 -4
- data/spec/mongo/collection_spec.rb +331 -14
- data/spec/mongo/cursor_spec.rb +117 -5
- data/spec/mongo/database_spec.rb +240 -8
- data/spec/mongo/error/operation_failure_spec.rb +47 -1
- data/spec/mongo/error/parser_spec.rb +160 -23
- data/spec/mongo/operation/insert/bulk_spec.rb +2 -1
- data/spec/mongo/operation/result_spec.rb +27 -0
- data/spec/mongo/operation/update/bulk_spec.rb +1 -0
- data/spec/mongo/retryable_spec.rb +2 -0
- data/spec/mongo/server/app_metadata_spec.rb +2 -2
- data/spec/mongo/server/connection_spec.rb +13 -17
- data/spec/mongo/server/monitor/connection_spec.rb +13 -10
- data/spec/mongo/server_selector_spec.rb +34 -2
- data/spec/mongo/session/session_pool_spec.rb +14 -3
- data/spec/mongo/session_spec.rb +3 -3
- data/spec/mongo/session_transaction_spec.rb +4 -3
- data/spec/mongo/socket/ssl_spec.rb +19 -5
- data/spec/mongo/socket_spec.rb +1 -62
- data/spec/mongo/uri/srv_protocol_spec.rb +14 -20
- data/spec/mongo/uri_option_parsing_spec.rb +94 -8
- data/spec/mongo/uri_spec.rb +23 -10
- data/spec/mongo/write_concern_spec.rb +56 -3
- data/spec/spec_tests/change_streams_spec.rb +2 -1
- data/spec/spec_tests/cmap_spec.rb +1 -1
- data/spec/spec_tests/crud_spec.rb +12 -2
- data/spec/spec_tests/data/change_streams/change-streams-errors.yml +24 -1
- data/spec/spec_tests/data/change_streams/change-streams.yml +172 -3
- data/spec/spec_tests/data/command_monitoring/bulkWrite.yml +1 -1
- data/spec/spec_tests/data/command_monitoring/updateMany.yml +0 -2
- data/spec/spec_tests/data/command_monitoring/updateOne.yml +0 -5
- data/spec/spec_tests/data/crud/read/aggregate-out.yml +0 -6
- data/spec/spec_tests/data/crud/read/count-empty.yml +29 -0
- data/spec/spec_tests/data/crud/write/bulkWrite-arrayFilters.yml +1 -0
- data/spec/spec_tests/data/crud/write/bulkWrite-collation.yml +101 -0
- data/spec/spec_tests/data/crud/write/bulkWrite.yml +401 -0
- data/spec/spec_tests/data/crud/write/insertMany.yml +58 -2
- data/spec/spec_tests/data/crud/write/updateMany-arrayFilters.yml +3 -0
- data/spec/spec_tests/data/crud/write/updateOne-arrayFilters.yml +6 -1
- data/spec/spec_tests/data/crud_v2/aggregate-merge.yml +103 -0
- data/spec/spec_tests/data/crud_v2/aggregate-out-readConcern.yml +110 -0
- data/spec/spec_tests/data/crud_v2/bulkWrite-arrayFilters.yml +81 -0
- data/spec/spec_tests/data/crud_v2/db-aggregate.yml +38 -0
- data/spec/spec_tests/data/crud_v2/updateWithPipelines.yml +92 -0
- data/spec/spec_tests/data/retryable_writes/insertOne-serverErrors.yml +2 -2
- data/spec/spec_tests/data/transactions/abort.yml +3 -0
- data/spec/spec_tests/data/transactions/bulk.yml +3 -8
- data/spec/spec_tests/data/transactions/causal-consistency.yml +3 -8
- data/spec/spec_tests/data/transactions/commit.yml +3 -1
- data/spec/spec_tests/data/transactions/count.yml +3 -0
- data/spec/spec_tests/data/transactions/delete.yml +3 -0
- data/spec/spec_tests/data/transactions/error-labels.yml +4 -1
- data/spec/spec_tests/data/transactions/errors-client.yml +56 -0
- data/spec/spec_tests/data/transactions/errors.yml +3 -0
- data/spec/spec_tests/data/transactions/findOneAndDelete.yml +3 -0
- data/spec/spec_tests/data/transactions/findOneAndReplace.yml +3 -0
- data/spec/spec_tests/data/transactions/findOneAndUpdate.yml +3 -0
- data/spec/spec_tests/data/transactions/insert.yml +3 -0
- data/spec/spec_tests/data/transactions/isolation.yml +3 -0
- data/spec/spec_tests/data/transactions/mongos-pin-auto.yml +1671 -0
- data/spec/spec_tests/data/transactions/mongos-recovery-token.yml +347 -0
- data/spec/spec_tests/data/transactions/pin-mongos.yml +557 -0
- data/spec/spec_tests/data/transactions/read-concern.yml +3 -0
- data/spec/spec_tests/data/transactions/read-pref.yml +3 -0
- data/spec/spec_tests/data/transactions/reads.yml +3 -0
- data/spec/spec_tests/data/transactions/retryable-abort.yml +5 -2
- data/spec/spec_tests/data/transactions/retryable-commit.yml +4 -1
- data/spec/spec_tests/data/transactions/retryable-writes.yml +3 -0
- data/spec/spec_tests/data/transactions/run-command.yml +3 -0
- data/spec/spec_tests/data/transactions/transaction-options.yml +6 -0
- data/spec/spec_tests/data/transactions/update.yml +3 -8
- data/spec/spec_tests/data/transactions/write-concern.yml +348 -38
- data/spec/spec_tests/data/transactions_api/callback-aborts.yml +6 -0
- data/spec/spec_tests/data/transactions_api/callback-commits.yml +5 -0
- data/spec/spec_tests/data/transactions_api/callback-retry.yml +7 -2
- data/spec/spec_tests/data/transactions_api/commit-retry.yml +70 -15
- data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror-4.2.yml +3 -0
- data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror.yml +3 -0
- data/spec/spec_tests/data/transactions_api/commit-writeconcernerror.yml +59 -109
- data/spec/spec_tests/data/transactions_api/commit.yml +5 -0
- data/spec/spec_tests/data/transactions_api/transaction-options.yml +10 -0
- data/spec/spec_tests/retryable_reads_spec.rb +5 -2
- data/spec/spec_tests/retryable_writes_spec.rb +5 -2
- data/spec/spec_tests/sdam_monitoring_spec.rb +3 -3
- data/spec/spec_tests/sdam_spec.rb +2 -2
- data/spec/spec_tests/transactions_api_spec.rb +1 -67
- data/spec/spec_tests/transactions_spec.rb +2 -66
- data/spec/support/authorization.rb +4 -0
- data/spec/support/change_streams.rb +30 -10
- data/spec/support/change_streams/operation.rb +27 -0
- data/spec/support/client_registry.rb +44 -25
- data/spec/support/cluster_config.rb +25 -14
- data/spec/support/cluster_tools.rb +32 -10
- data/spec/support/command_monitoring.rb +1 -1
- data/spec/support/common_shortcuts.rb +30 -0
- data/spec/support/connection_string.rb +8 -3
- data/spec/support/constraints.rb +34 -0
- data/spec/support/crud.rb +31 -16
- data/spec/support/crud/context.rb +23 -0
- data/spec/support/crud/operation.rb +311 -14
- data/spec/support/crud/spec.rb +2 -1
- data/spec/support/crud/test.rb +24 -27
- data/spec/support/crud/test_base.rb +22 -0
- data/spec/support/crud/verifier.rb +15 -1
- data/spec/support/event_subscriber.rb +12 -0
- data/spec/support/sdam_formatter_integration.rb +12 -6
- data/spec/support/shared/server_selector.rb +10 -0
- data/spec/support/shared/session.rb +13 -12
- data/spec/support/spec_config.rb +32 -22
- data/spec/support/spec_setup.rb +2 -2
- data/spec/support/transactions.rb +87 -0
- data/spec/support/transactions/context.rb +33 -0
- data/spec/support/transactions/operation.rb +99 -349
- data/spec/support/transactions/spec.rb +1 -3
- data/spec/support/transactions/test.rb +110 -49
- data/spec/support/utils.rb +74 -1
- metadata +52 -10
- metadata.gz.sig +0 -0
- data/spec/support/crud/read.rb +0 -265
- data/spec/support/crud/write.rb +0 -284
data/lib/mongo/session.rb
CHANGED
@@ -20,36 +20,196 @@ module Mongo
|
|
20
20
|
# A logical session representing a set of sequential operations executed
|
21
21
|
# by an application that are related in some way.
|
22
22
|
#
|
23
|
+
# @note Session objects are not thread-safe. An application may use a session
|
24
|
+
# from only one thread or process at a time.
|
25
|
+
#
|
23
26
|
# @since 2.5.0
|
24
27
|
class Session
|
25
28
|
extend Forwardable
|
26
29
|
include Retryable
|
27
30
|
include Loggable
|
31
|
+
include ClusterTime::Consumer
|
32
|
+
|
33
|
+
# Initialize a Session.
|
34
|
+
#
|
35
|
+
# @note Applications should use Client#start_session to begin a session.
|
36
|
+
#
|
37
|
+
# @example
|
38
|
+
# Session.new(server_session, client, options)
|
39
|
+
#
|
40
|
+
# @param [ ServerSession ] server_session The server session this session is associated with.
|
41
|
+
# @param [ Client ] client The client through which this session is created.
|
42
|
+
# @param [ Hash ] options The options for this session.
|
43
|
+
#
|
44
|
+
# @option options [ true|false ] :causal_consistency Whether to enable
|
45
|
+
# causal consistency for this session.
|
46
|
+
# @option options [ Hash ] :default_transaction_options Options to pass
|
47
|
+
# to start_transaction by default, can contain any of the options that
|
48
|
+
# start_transaction accepts.
|
49
|
+
# @option options [ true|false ] :implicit For internal driver use only -
|
50
|
+
# specifies whether the session is implicit.
|
51
|
+
# @option options [ Hash ] :read_preference The read preference options hash,
|
52
|
+
# with the following optional keys:
|
53
|
+
# - *:mode* -- the read preference as a string or symbol; valid values are
|
54
|
+
# *:primary*, *:primary_preferred*, *:secondary*, *:secondary_preferred*
|
55
|
+
# and *:nearest*.
|
56
|
+
#
|
57
|
+
# @since 2.5.0
|
58
|
+
# @api private
|
59
|
+
def initialize(server_session, client, options = {})
|
60
|
+
@server_session = server_session
|
61
|
+
options = options.dup
|
62
|
+
|
63
|
+
@client = client.use(:admin)
|
64
|
+
@options = options.freeze
|
65
|
+
@cluster_time = nil
|
66
|
+
@state = NO_TRANSACTION_STATE
|
67
|
+
end
|
28
68
|
|
29
|
-
#
|
69
|
+
# @return [ Hash ] The options for this session.
|
30
70
|
#
|
31
71
|
# @since 2.5.0
|
32
72
|
attr_reader :options
|
33
73
|
|
34
|
-
#
|
74
|
+
# @return [ Client ] The client through which this session was created.
|
35
75
|
#
|
36
76
|
# @since 2.5.1
|
37
77
|
attr_reader :client
|
38
78
|
|
39
|
-
|
79
|
+
def cluster
|
80
|
+
@client.cluster
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [ BSON::Timestamp ] The latest seen operation time for this session.
|
40
84
|
#
|
41
85
|
# @since 2.5.0
|
42
|
-
attr_reader :
|
86
|
+
attr_reader :operation_time
|
87
|
+
|
88
|
+
# @return [ Hash ] The options for the transaction currently being executed
|
89
|
+
# on this session.
|
90
|
+
#
|
91
|
+
# @since 2.6.0
|
92
|
+
def txn_options
|
93
|
+
@txn_options or raise ArgumentError, "There is no active transaction"
|
94
|
+
end
|
95
|
+
|
96
|
+
# Is this session an implicit one (not user-created).
|
97
|
+
#
|
98
|
+
# @example Is the session implicit?
|
99
|
+
# session.implicit?
|
100
|
+
#
|
101
|
+
# @return [ true, false ] Whether this session is implicit.
|
102
|
+
#
|
103
|
+
# @since 2.5.1
|
104
|
+
def implicit?
|
105
|
+
@implicit ||= !!(@options.key?(:implicit) && @options[:implicit] == true)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Is this session an explicit one (i.e. user-created).
|
109
|
+
#
|
110
|
+
# @example Is the session explicit?
|
111
|
+
# session.explicit?
|
112
|
+
#
|
113
|
+
# @return [ true, false ] Whether this session is explicit.
|
114
|
+
#
|
115
|
+
# @since 2.5.2
|
116
|
+
def explicit?
|
117
|
+
!implicit?
|
118
|
+
end
|
119
|
+
|
120
|
+
# Whether reads executed with this session can be retried according to
|
121
|
+
# the modern retryable reads specification.
|
122
|
+
#
|
123
|
+
# If this method returns true, the modern retryable reads have been
|
124
|
+
# requested by the application. If the server selected for a read operation
|
125
|
+
# supports modern retryable reads, they will be used for that particular
|
126
|
+
# operation. If the server selected for a read operation does not support
|
127
|
+
# modern retryable reads, the read will not be retried.
|
128
|
+
#
|
129
|
+
# If this method returns false, legacy retryable reads have been requested
|
130
|
+
# by the application. Legacy retryable read logic will be used regardless
|
131
|
+
# of server version of the server(s) that the client is connected to.
|
132
|
+
# The number of read retries is given by :max_read_retries client option,
|
133
|
+
# which is 1 by default and can be set to 0 to disable legacy read retries.
|
134
|
+
#
|
135
|
+
# @api private
|
136
|
+
def retry_reads?
|
137
|
+
client.options[:retry_reads] != false
|
138
|
+
end
|
43
139
|
|
44
|
-
#
|
140
|
+
# Will writes executed with this session be retried.
|
141
|
+
#
|
142
|
+
# @example Will writes be retried.
|
143
|
+
# session.retry_writes?
|
144
|
+
#
|
145
|
+
# @return [ true, false ] If writes will be retried.
|
146
|
+
#
|
147
|
+
# @note Retryable writes are only available on server versions at least 3.6
|
148
|
+
# and with sharded clusters or replica sets.
|
45
149
|
#
|
46
150
|
# @since 2.5.0
|
47
|
-
|
151
|
+
def retry_writes?
|
152
|
+
!!client.options[:retry_writes] && (cluster.replica_set? || cluster.sharded?)
|
153
|
+
end
|
48
154
|
|
49
|
-
#
|
155
|
+
# Get the read preference the session will use in the currently
|
156
|
+
# active transaction.
|
157
|
+
#
|
158
|
+
# This is a driver style hash with underscore keys.
|
159
|
+
#
|
160
|
+
# @example Get the transaction's read preference
|
161
|
+
# session.txn_read_preference
|
162
|
+
#
|
163
|
+
# @return [ Hash ] The read preference of the transaction.
|
50
164
|
#
|
51
165
|
# @since 2.6.0
|
52
|
-
|
166
|
+
def txn_read_preference
|
167
|
+
rp = txn_options[:read] ||
|
168
|
+
@client.read_preference
|
169
|
+
Mongo::Lint.validate_underscore_read_preference(rp)
|
170
|
+
rp
|
171
|
+
end
|
172
|
+
|
173
|
+
# Whether this session has ended.
|
174
|
+
#
|
175
|
+
# @example
|
176
|
+
# session.ended?
|
177
|
+
#
|
178
|
+
# @return [ true, false ] Whether the session has ended.
|
179
|
+
#
|
180
|
+
# @since 2.5.0
|
181
|
+
def ended?
|
182
|
+
@server_session.nil?
|
183
|
+
end
|
184
|
+
|
185
|
+
# Get the server session id of this session, if the session was not ended.
|
186
|
+
# If the session was ended, returns nil.
|
187
|
+
#
|
188
|
+
# @example Get the session id.
|
189
|
+
# session.session_id
|
190
|
+
#
|
191
|
+
# @return [ BSON::Document ] The server session id.
|
192
|
+
#
|
193
|
+
# @since 2.5.0
|
194
|
+
def session_id
|
195
|
+
if ended?
|
196
|
+
raise Error::SessionEnded
|
197
|
+
end
|
198
|
+
|
199
|
+
@server_session.session_id
|
200
|
+
end
|
201
|
+
|
202
|
+
# @return [ Server | nil ] The server (which should be a mongos) that this
|
203
|
+
# session is pinned to, if any.
|
204
|
+
#
|
205
|
+
# @api private
|
206
|
+
attr_reader :pinned_server
|
207
|
+
|
208
|
+
# @return [ BSON::Document | nil ] Recovery token for the sharded
|
209
|
+
# transaction being executed on this session, if any.
|
210
|
+
#
|
211
|
+
# @api private
|
212
|
+
attr_accessor :recovery_token
|
53
213
|
|
54
214
|
# Error message indicating that the session was retrieved from a client with a different cluster than that of the
|
55
215
|
# client through which it is currently being used.
|
@@ -98,53 +258,12 @@ module Mongo
|
|
98
258
|
# @since 2.6.0
|
99
259
|
TRANSACTION_ABORTED_STATE = :transaction_aborted
|
100
260
|
|
261
|
+
# @api private
|
101
262
|
UNLABELED_WRITE_CONCERN_CODES = [
|
102
263
|
79, # UnknownReplWriteConcern
|
103
264
|
100, # CannotSatisfyWriteConcern,
|
104
265
|
].freeze
|
105
266
|
|
106
|
-
# Initialize a Session.
|
107
|
-
#
|
108
|
-
# @note Applications should use Client#start_session to begin a session.
|
109
|
-
#
|
110
|
-
# @example
|
111
|
-
# Session.new(server_session, client, options)
|
112
|
-
#
|
113
|
-
# @param [ ServerSession ] server_session The server session this session is associated with.
|
114
|
-
# @param [ Client ] client The client through which this session is created.
|
115
|
-
# @param [ Hash ] options The options for this session.
|
116
|
-
#
|
117
|
-
# @option options [ true|false ] :causal_consistency Whether to enable
|
118
|
-
# causal consistency for this session.
|
119
|
-
# @option options [ Hash ] :default_transaction_options Options to pass
|
120
|
-
# to start_transaction by default, can contain any of the options that
|
121
|
-
# start_transaction accepts.
|
122
|
-
# @option options [ true|false ] :implicit For internal driver use only -
|
123
|
-
# specifies whether the session is implicit.
|
124
|
-
# @option options [ Hash ] :read_preference The read preference options hash,
|
125
|
-
# with the following optional keys:
|
126
|
-
# - *:mode* -- the read preference as a string or symbol; valid values are
|
127
|
-
# *:primary*, *:primary_preferred*, *:secondary*, *:secondary_preferred*
|
128
|
-
# and *:nearest*.
|
129
|
-
#
|
130
|
-
# @since 2.5.0
|
131
|
-
# @api private
|
132
|
-
def initialize(server_session, client, options = {})
|
133
|
-
@server_session = server_session
|
134
|
-
options = options.dup
|
135
|
-
|
136
|
-
# Because the read preference will need to be inserted into a command as a string, we convert
|
137
|
-
# it from a symbol immediately upon receiving it.
|
138
|
-
if options[:read_preference] && options[:read_preference][:mode]
|
139
|
-
options[:read_preference][:mode] = options[:read_preference][:mode].to_s
|
140
|
-
end
|
141
|
-
|
142
|
-
@client = client.use(:admin)
|
143
|
-
@options = options.freeze
|
144
|
-
@cluster_time = nil
|
145
|
-
@state = NO_TRANSACTION_STATE
|
146
|
-
end
|
147
|
-
|
148
267
|
# Get a formatted string for use in inspection.
|
149
268
|
#
|
150
269
|
# @example Inspect the session object.
|
@@ -159,6 +278,16 @@ module Mongo
|
|
159
278
|
|
160
279
|
# End this session.
|
161
280
|
#
|
281
|
+
# If there is an in-progress transaction on this session, the transaction
|
282
|
+
# is aborted. The server session associated with this session is returned
|
283
|
+
# to the server session pool. Finally, this session is marked ended and
|
284
|
+
# is no longer usable.
|
285
|
+
#
|
286
|
+
# If this session is already ended, this method does nothing.
|
287
|
+
#
|
288
|
+
# Note that this method does not directly issue an endSessions command
|
289
|
+
# to this server, contrary to what its name might suggest.
|
290
|
+
#
|
162
291
|
# @example
|
163
292
|
# session.end_session
|
164
293
|
#
|
@@ -179,377 +308,142 @@ module Mongo
|
|
179
308
|
@server_session = nil
|
180
309
|
end
|
181
310
|
|
182
|
-
#
|
183
|
-
#
|
184
|
-
# @example
|
185
|
-
# session.ended?
|
311
|
+
# Executes the provided block in a transaction, retrying as necessary.
|
186
312
|
#
|
187
|
-
#
|
313
|
+
# Returns the return value of the block.
|
188
314
|
#
|
189
|
-
#
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
#
|
315
|
+
# Exact number of retries and when they are performed are implementation
|
316
|
+
# details of the driver; the provided block should be idempotent, and
|
317
|
+
# should be prepared to be called more than once. The driver may retry
|
318
|
+
# the commit command within an active transaction or it may repeat the
|
319
|
+
# transaction and invoke the block again, depending on the error
|
320
|
+
# encountered if any. Note also that the retries may be executed against
|
321
|
+
# different servers.
|
195
322
|
#
|
196
|
-
#
|
197
|
-
#
|
323
|
+
# Transactions cannot be nested - InvalidTransactionOperation will be raised
|
324
|
+
# if this method is called when the session already has an active transaction.
|
198
325
|
#
|
199
|
-
#
|
326
|
+
# Exceptions raised by the block which are not derived from Mongo::Error
|
327
|
+
# stop processing, abort the transaction and are propagated out of
|
328
|
+
# with_transaction. Exceptions derived from Mongo::Error may be
|
329
|
+
# handled by with_transaction, resulting in retries of the process.
|
200
330
|
#
|
201
|
-
#
|
202
|
-
#
|
203
|
-
|
204
|
-
|
205
|
-
c[:autocommit] = false if in_transaction?
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
# Add this session's id to a command document.
|
331
|
+
# Currently, with_transaction will retry commits and block invocations
|
332
|
+
# until at least 120 seconds have passed since with_transaction started
|
333
|
+
# executing. This timeout is not configurable and may change in a future
|
334
|
+
# driver version.
|
210
335
|
#
|
211
|
-
# @
|
212
|
-
#
|
336
|
+
# @note with_transaction contains a loop, therefore the if with_transaction
|
337
|
+
# itself is placed in a loop, its block should not call next or break to
|
338
|
+
# control the outer loop because this will instead affect the loop in
|
339
|
+
# with_transaction. The driver will warn and abort the transaction
|
340
|
+
# if it detects this situation.
|
213
341
|
#
|
214
|
-
# @
|
342
|
+
# @example Execute a statement in a transaction
|
343
|
+
# session.with_transaction(write_concern: {w: :majority}) do
|
344
|
+
# collection.update_one({ id: 3 }, { '$set' => { status: 'Inactive'} },
|
345
|
+
# session: session)
|
215
346
|
#
|
216
|
-
#
|
217
|
-
# @api private
|
218
|
-
def add_id!(command)
|
219
|
-
command.merge!(lsid: session_id)
|
220
|
-
end
|
221
|
-
|
222
|
-
# Add the startTransaction field to a command document if applicable.
|
347
|
+
# end
|
223
348
|
#
|
224
|
-
# @example
|
225
|
-
#
|
226
|
-
#
|
227
|
-
#
|
228
|
-
#
|
229
|
-
# @since 2.6.0
|
230
|
-
# @api private
|
231
|
-
def add_start_transaction!(command)
|
232
|
-
command.tap do |c|
|
233
|
-
if starting_transaction?
|
234
|
-
c[:startTransaction] = true
|
235
|
-
end
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
# Add the transaction number to a command document if applicable.
|
240
|
-
#
|
241
|
-
# @example
|
242
|
-
# session.add_txn_num!(cmd)
|
243
|
-
#
|
244
|
-
# @return [ Hash, BSON::Document ] The command document.
|
349
|
+
# @example Execute a statement in a transaction, limiting total time consumed
|
350
|
+
# Timeout.timeout(5) do
|
351
|
+
# session.with_transaction(write_concern: {w: :majority}) do
|
352
|
+
# collection.update_one({ id: 3 }, { '$set' => { status: 'Inactive'} },
|
353
|
+
# session: session)
|
245
354
|
#
|
246
|
-
#
|
247
|
-
#
|
248
|
-
def add_txn_num!(command)
|
249
|
-
command.tap do |c|
|
250
|
-
c[:txnNumber] = BSON::Int64.new(@server_session.txn_num) if in_transaction?
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
|
-
# Add the transactions options if applicable.
|
355
|
+
# end
|
356
|
+
# end
|
255
357
|
#
|
256
|
-
# @
|
257
|
-
#
|
358
|
+
# @param [ Hash ] options The options for the transaction being started.
|
359
|
+
# These are the same options that start_transaction accepts.
|
258
360
|
#
|
259
|
-
# @
|
361
|
+
# @raise [ Error::InvalidTransactionOperation ] If a transaction is already in
|
362
|
+
# progress or if the write concern is unacknowledged.
|
260
363
|
#
|
261
|
-
# @since 2.
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
Mongo::Lint.validate_camel_case_read_preference(txn_read_pref)
|
271
|
-
c['$readPreference'] = txn_read_pref
|
272
|
-
end
|
273
|
-
|
274
|
-
# The read concern should be added to any command that starts a transaction.
|
275
|
-
if starting_transaction?
|
276
|
-
# https://jira.mongodb.org/browse/SPEC-1161: transaction's
|
277
|
-
# read concern overrides collection/database/client read concerns,
|
278
|
-
# even if transaction's read concern is not set.
|
279
|
-
# Read concern here is the one sent to the server and may
|
280
|
-
# include afterClusterTime.
|
281
|
-
if rc = c[:readConcern]
|
282
|
-
rc = rc.dup
|
283
|
-
rc.delete(:level)
|
284
|
-
end
|
285
|
-
if txn_read_concern
|
286
|
-
if rc
|
287
|
-
rc.update(txn_read_concern)
|
288
|
-
else
|
289
|
-
rc = txn_read_concern.dup
|
290
|
-
end
|
291
|
-
end
|
292
|
-
if rc.nil? || rc.empty?
|
293
|
-
c.delete(:readConcern)
|
294
|
-
else
|
295
|
-
c[:readConcern ] = rc
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
# We need to send the read concern level as a string rather than a symbol.
|
300
|
-
if c[:readConcern] && c[:readConcern][:level]
|
301
|
-
c[:readConcern][:level] = c[:readConcern][:level].to_s
|
364
|
+
# @since 2.7.0
|
365
|
+
def with_transaction(options=nil)
|
366
|
+
# Non-configurable 120 second timeout for the entire operation
|
367
|
+
deadline = Time.now + 120
|
368
|
+
transaction_in_progress = false
|
369
|
+
loop do
|
370
|
+
commit_options = {}
|
371
|
+
if options
|
372
|
+
commit_options[:write_concern] = options[:write_concern]
|
302
373
|
end
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
elsif txn_write_concern
|
312
|
-
c[:writeConcern] ||= txn_write_concern
|
374
|
+
start_transaction(options)
|
375
|
+
transaction_in_progress = true
|
376
|
+
begin
|
377
|
+
rv = yield self
|
378
|
+
rescue Exception => e
|
379
|
+
if within_states?(STARTING_TRANSACTION_STATE, TRANSACTION_IN_PROGRESS_STATE)
|
380
|
+
abort_transaction
|
381
|
+
transaction_in_progress = false
|
313
382
|
end
|
314
|
-
end
|
315
|
-
|
316
|
-
# A non-numeric write concern w value needs to be sent as a string rather than a symbol.
|
317
|
-
if c[:writeConcern] && c[:writeConcern][:w] && c[:writeConcern][:w].is_a?(Symbol)
|
318
|
-
c[:writeConcern][:w] = c[:writeConcern][:w].to_s
|
319
|
-
end
|
320
|
-
end
|
321
|
-
end
|
322
|
-
|
323
|
-
# Remove the read concern and/or write concern from the command if not applicable.
|
324
|
-
#
|
325
|
-
# @example
|
326
|
-
# session.suppress_read_write_concern!(cmd)
|
327
|
-
#
|
328
|
-
# @return [ Hash, BSON::Document ] The command document.
|
329
|
-
#
|
330
|
-
# @since 2.6.0
|
331
|
-
# @api private
|
332
|
-
def suppress_read_write_concern!(command)
|
333
|
-
command.tap do |c|
|
334
|
-
next unless in_transaction?
|
335
|
-
|
336
|
-
c.delete(:readConcern) unless starting_transaction?
|
337
|
-
c.delete(:writeConcern) unless c[:commitTransaction] || c[:abortTransaction]
|
338
|
-
end
|
339
|
-
end
|
340
|
-
|
341
|
-
# Ensure that the read preference of a command primary.
|
342
|
-
#
|
343
|
-
# @example
|
344
|
-
# session.validate_read_preference!(command)
|
345
|
-
#
|
346
|
-
# @raise [ Mongo::Error::InvalidTransactionOperation ] If the read preference of the command is
|
347
|
-
# not primary.
|
348
|
-
#
|
349
|
-
# @since 2.6.0
|
350
|
-
# @api private
|
351
|
-
def validate_read_preference!(command)
|
352
|
-
return unless in_transaction? && non_primary_read_preference_mode?(command)
|
353
|
-
|
354
|
-
raise Mongo::Error::InvalidTransactionOperation.new(
|
355
|
-
Mongo::Error::InvalidTransactionOperation::INVALID_READ_PREFERENCE)
|
356
|
-
end
|
357
|
-
|
358
|
-
# Update the state of the session due to a (non-commit and non-abort) operation being run.
|
359
|
-
#
|
360
|
-
# @since 2.6.0
|
361
|
-
# @api private
|
362
|
-
def update_state!
|
363
|
-
case @state
|
364
|
-
when STARTING_TRANSACTION_STATE
|
365
|
-
@state = TRANSACTION_IN_PROGRESS_STATE
|
366
|
-
when TRANSACTION_COMMITTED_STATE, TRANSACTION_ABORTED_STATE
|
367
|
-
@state = NO_TRANSACTION_STATE
|
368
|
-
end
|
369
|
-
end
|
370
|
-
|
371
|
-
# Validate the session.
|
372
|
-
#
|
373
|
-
# @example
|
374
|
-
# session.validate!(cluster)
|
375
|
-
#
|
376
|
-
# @param [ Cluster ] cluster The cluster the session is attempted to be used with.
|
377
|
-
#
|
378
|
-
# @return [ nil ] nil if the session is valid.
|
379
|
-
#
|
380
|
-
# @raise [ Mongo::Error::InvalidSession ] Raise error if the session is not valid.
|
381
|
-
#
|
382
|
-
# @since 2.5.0
|
383
|
-
# @api private
|
384
|
-
def validate!(cluster)
|
385
|
-
check_matching_cluster!(cluster)
|
386
|
-
check_if_ended!
|
387
|
-
self
|
388
|
-
end
|
389
|
-
|
390
|
-
# Process a response from the server that used this session.
|
391
|
-
#
|
392
|
-
# @example Process a response from the server.
|
393
|
-
# session.process(result)
|
394
|
-
#
|
395
|
-
# @param [ Operation::Result ] result The result from the operation.
|
396
|
-
#
|
397
|
-
# @return [ Operation::Result ] The result.
|
398
|
-
#
|
399
|
-
# @since 2.5.0
|
400
|
-
# @api private
|
401
|
-
def process(result)
|
402
|
-
unless implicit?
|
403
|
-
set_operation_time(result)
|
404
|
-
set_cluster_time(result)
|
405
|
-
end
|
406
|
-
@server_session.set_last_use!
|
407
|
-
result
|
408
|
-
end
|
409
|
-
|
410
|
-
# Advance the cached cluster time document for this session.
|
411
|
-
#
|
412
|
-
# @example Advance the cluster time.
|
413
|
-
# session.advance_cluster_time(doc)
|
414
|
-
#
|
415
|
-
# @param [ BSON::Document, Hash ] new_cluster_time The new cluster time.
|
416
|
-
#
|
417
|
-
# @return [ BSON::Document, Hash ] The new cluster time.
|
418
|
-
#
|
419
|
-
# @since 2.5.0
|
420
|
-
def advance_cluster_time(new_cluster_time)
|
421
|
-
if @cluster_time
|
422
|
-
@cluster_time = [ @cluster_time, new_cluster_time ].max_by { |doc| doc[Cluster::CLUSTER_TIME] }
|
423
|
-
else
|
424
|
-
@cluster_time = new_cluster_time
|
425
|
-
end
|
426
|
-
end
|
427
|
-
|
428
|
-
# Advance the cached operation time for this session.
|
429
|
-
#
|
430
|
-
# @example Advance the operation time.
|
431
|
-
# session.advance_operation_time(timestamp)
|
432
|
-
#
|
433
|
-
# @param [ BSON::Timestamp ] new_operation_time The new operation time.
|
434
|
-
#
|
435
|
-
# @return [ BSON::Timestamp ] The max operation time, considering the current and new times.
|
436
|
-
#
|
437
|
-
# @since 2.5.0
|
438
|
-
def advance_operation_time(new_operation_time)
|
439
|
-
if @operation_time
|
440
|
-
@operation_time = [ @operation_time, new_operation_time ].max
|
441
|
-
else
|
442
|
-
@operation_time = new_operation_time
|
443
|
-
end
|
444
|
-
end
|
445
|
-
|
446
|
-
# Whether reads executed with this session can be retried according to
|
447
|
-
# the modern retryable reads specification.
|
448
|
-
#
|
449
|
-
# If this method returns true, the modern retryable reads have been
|
450
|
-
# requested by the application. If the server selected for a read operation
|
451
|
-
# supports modern retryable reads, they will be used for that particular
|
452
|
-
# operation. If the server selected for a read operation does not support
|
453
|
-
# modern retryable reads, the read will not be retried.
|
454
|
-
#
|
455
|
-
# If this method returns false, legacy retryable reads have been requested
|
456
|
-
# by the application. Legacy retryable read logic will be used regardless
|
457
|
-
# of server version of the server(s) that the client is connected to.
|
458
|
-
# The number of read retries is given by :max_read_retries client option,
|
459
|
-
# which is 1 by default and can be set to 0 to disable legacy read retries.
|
460
|
-
#
|
461
|
-
# @api private
|
462
|
-
def retry_reads?
|
463
|
-
client.options[:retry_reads] != false
|
464
|
-
end
|
465
|
-
|
466
|
-
# Will writes executed with this session be retried.
|
467
|
-
#
|
468
|
-
# @example Will writes be retried.
|
469
|
-
# session.retry_writes?
|
470
|
-
#
|
471
|
-
# @return [ true, false ] If writes will be retried.
|
472
|
-
#
|
473
|
-
# @note Retryable writes are only available on server versions at least 3.6
|
474
|
-
# and with sharded clusters or replica sets.
|
475
|
-
#
|
476
|
-
# @since 2.5.0
|
477
|
-
def retry_writes?
|
478
|
-
!!client.options[:retry_writes] && (cluster.replica_set? || cluster.sharded?)
|
479
|
-
end
|
480
|
-
|
481
|
-
# Get the server session id of this session, if the session was not ended.
|
482
|
-
# If the session was ended, returns nil.
|
483
|
-
#
|
484
|
-
# @example Get the session id.
|
485
|
-
# session.session_id
|
486
|
-
#
|
487
|
-
# @return [ BSON::Document ] The server session id.
|
488
|
-
#
|
489
|
-
# @since 2.5.0
|
490
|
-
def session_id
|
491
|
-
if ended?
|
492
|
-
raise Error::SessionEnded
|
493
|
-
end
|
494
|
-
|
495
|
-
@server_session.session_id
|
496
|
-
end
|
497
|
-
|
498
|
-
# Increment and return the next transaction number.
|
499
|
-
#
|
500
|
-
# @example Get the next transaction number.
|
501
|
-
# session.next_txn_num
|
502
|
-
#
|
503
|
-
# @return [ Integer ] The next transaction number.
|
504
|
-
#
|
505
|
-
# @since 2.5.0
|
506
|
-
# @api private
|
507
|
-
def next_txn_num
|
508
|
-
if ended?
|
509
|
-
raise Error::SessionEnded
|
510
|
-
end
|
511
|
-
|
512
|
-
@server_session.next_txn_num
|
513
|
-
end
|
514
383
|
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
#
|
520
|
-
# @return [ Integer ] The current transaction number.
|
521
|
-
#
|
522
|
-
# @since 2.6.0
|
523
|
-
def txn_num
|
524
|
-
if ended?
|
525
|
-
raise Error::SessionEnded
|
526
|
-
end
|
384
|
+
if Time.now >= deadline
|
385
|
+
transaction_in_progress = false
|
386
|
+
raise
|
387
|
+
end
|
527
388
|
|
528
|
-
|
529
|
-
|
389
|
+
if e.is_a?(Mongo::Error) && e.label?('TransientTransactionError')
|
390
|
+
next
|
391
|
+
end
|
530
392
|
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
#
|
538
|
-
# @since 2.5.1
|
539
|
-
def implicit?
|
540
|
-
@implicit ||= !!(@options.key?(:implicit) && @options[:implicit] == true)
|
541
|
-
end
|
393
|
+
raise
|
394
|
+
else
|
395
|
+
if within_states?(TRANSACTION_ABORTED_STATE, NO_TRANSACTION_STATE, TRANSACTION_COMMITTED_STATE)
|
396
|
+
transaction_in_progress = false
|
397
|
+
return rv
|
398
|
+
end
|
542
399
|
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
400
|
+
begin
|
401
|
+
commit_transaction(commit_options)
|
402
|
+
transaction_in_progress = false
|
403
|
+
return rv
|
404
|
+
rescue Mongo::Error => e
|
405
|
+
if e.label?('UnknownTransactionCommitResult')
|
406
|
+
if Time.now >= deadline ||
|
407
|
+
e.is_a?(Error::OperationFailure) && e.max_time_ms_expired?
|
408
|
+
then
|
409
|
+
transaction_in_progress = false
|
410
|
+
raise
|
411
|
+
end
|
412
|
+
wc_options = case v = commit_options[:write_concern]
|
413
|
+
when WriteConcern::Base
|
414
|
+
v.options
|
415
|
+
when nil
|
416
|
+
{}
|
417
|
+
else
|
418
|
+
v
|
419
|
+
end
|
420
|
+
commit_options[:write_concern] = wc_options.merge(w: :majority)
|
421
|
+
retry
|
422
|
+
elsif e.label?('TransientTransactionError')
|
423
|
+
if Time.now >= deadline
|
424
|
+
transaction_in_progress = false
|
425
|
+
raise
|
426
|
+
end
|
427
|
+
next
|
428
|
+
else
|
429
|
+
transaction_in_progress = false
|
430
|
+
raise
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
# No official return value, but return true so that in interactive
|
437
|
+
# use the method hints that it succeeded.
|
438
|
+
true
|
439
|
+
ensure
|
440
|
+
if transaction_in_progress
|
441
|
+
log_warn('with_transaction callback altered with_transaction loop, aborting transaction')
|
442
|
+
begin
|
443
|
+
abort_transaction
|
444
|
+
rescue Error::OperationFailure, Error::InvalidTransactionOperation
|
445
|
+
end
|
446
|
+
end
|
553
447
|
end
|
554
448
|
|
555
449
|
# Places subsequent operations in this session into a new transaction.
|
@@ -562,6 +456,8 @@ module Mongo
|
|
562
456
|
#
|
563
457
|
# @param [ Hash ] options The options for the transaction being started.
|
564
458
|
#
|
459
|
+
# @option options [ Integer ] :max_commit_time_ms The maximum amount of
|
460
|
+
# time to allow a single commitTransaction command to run, in milliseconds.
|
565
461
|
# @option options [ Hash ] read_concern The read concern options hash,
|
566
462
|
# with the following optional keys:
|
567
463
|
# - *:level* -- the read preference level as a symbol; valid values
|
@@ -580,6 +476,18 @@ module Mongo
|
|
580
476
|
def start_transaction(options = nil)
|
581
477
|
if options
|
582
478
|
Lint.validate_read_concern_option(options[:read_concern])
|
479
|
+
|
480
|
+
=begin
|
481
|
+
# It would be handy to detect invalid read preferences here, but
|
482
|
+
# some of the spec tests require later detection of invalid read prefs.
|
483
|
+
# Maybe we can do this when lint mode is on.
|
484
|
+
mode = options[:read] && options[:read][:mode].to_s
|
485
|
+
if mode && mode != 'primary'
|
486
|
+
raise Mongo::Error::InvalidTransactionOperation.new(
|
487
|
+
"read preference in a transaction must be primary (requested: #{mode})"
|
488
|
+
)
|
489
|
+
end
|
490
|
+
=end
|
583
491
|
end
|
584
492
|
|
585
493
|
check_if_ended!
|
@@ -589,16 +497,24 @@ module Mongo
|
|
589
497
|
Mongo::Error::InvalidTransactionOperation::TRANSACTION_ALREADY_IN_PROGRESS)
|
590
498
|
end
|
591
499
|
|
500
|
+
unpin
|
501
|
+
|
592
502
|
next_txn_num
|
593
|
-
@txn_options =
|
503
|
+
@txn_options = (@options[:default_transaction_options] || {}).merge(options || {})
|
594
504
|
|
595
|
-
if txn_write_concern && WriteConcern.
|
505
|
+
if txn_write_concern && !WriteConcern.get(txn_write_concern).acknowledged?
|
596
506
|
raise Mongo::Error::InvalidTransactionOperation.new(
|
597
507
|
Mongo::Error::InvalidTransactionOperation::UNACKNOWLEDGED_WRITE_CONCERN)
|
598
508
|
end
|
599
509
|
|
600
510
|
@state = STARTING_TRANSACTION_STATE
|
601
511
|
@already_committed = false
|
512
|
+
|
513
|
+
# This method has no explicit return value.
|
514
|
+
# We could return nil here but true indicates to the user that the
|
515
|
+
# operation succeeded. This is intended for interactive use.
|
516
|
+
# Note that the return value is not documented.
|
517
|
+
true
|
602
518
|
end
|
603
519
|
|
604
520
|
# Commit the currently active transaction on the session.
|
@@ -636,6 +552,7 @@ module Mongo
|
|
636
552
|
@last_commit_skipped = true
|
637
553
|
else
|
638
554
|
@last_commit_skipped = false
|
555
|
+
@committing_transaction = true
|
639
556
|
|
640
557
|
write_concern = options[:write_concern] || txn_options[:write_concern]
|
641
558
|
if write_concern && !write_concern.is_a?(WriteConcern::Base)
|
@@ -651,30 +568,24 @@ module Mongo
|
|
651
568
|
write_concern = WriteConcern.get(w: :majority, wtimeout: 10000)
|
652
569
|
end
|
653
570
|
end
|
654
|
-
|
571
|
+
spec = {
|
655
572
|
selector: { commitTransaction: 1 },
|
656
573
|
db_name: 'admin',
|
657
574
|
session: self,
|
658
575
|
txn_num: txn_num,
|
659
576
|
write_concern: write_concern,
|
660
|
-
|
577
|
+
}
|
578
|
+
Operation::Command.new(spec).execute(server)
|
661
579
|
end
|
662
580
|
end
|
663
|
-
rescue Mongo::Error::NoServerAvailable, Mongo::Error::SocketError => e
|
664
|
-
e.send(:add_label, Mongo::Error::UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)
|
665
|
-
raise e
|
666
|
-
rescue Mongo::Error::OperationFailure => e
|
667
|
-
err_doc = e.instance_variable_get(:@result).send(:first_document)
|
668
|
-
|
669
|
-
if e.write_retryable? || (err_doc['writeConcernError'] &&
|
670
|
-
!UNLABELED_WRITE_CONCERN_CODES.include?(err_doc['writeConcernError']['code']))
|
671
|
-
e.send(:add_label, Mongo::Error::UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)
|
672
|
-
end
|
673
|
-
|
674
|
-
raise e
|
675
581
|
ensure
|
676
582
|
@state = TRANSACTION_COMMITTED_STATE
|
583
|
+
@committing_transaction = false
|
677
584
|
end
|
585
|
+
|
586
|
+
# No official return value, but return true so that in interactive
|
587
|
+
# use the method hints that it succeeded.
|
588
|
+
true
|
678
589
|
end
|
679
590
|
|
680
591
|
# Abort the currently active transaction without making any changes to the database.
|
@@ -721,180 +632,365 @@ module Mongo
|
|
721
632
|
@state = TRANSACTION_ABORTED_STATE
|
722
633
|
raise
|
723
634
|
end
|
635
|
+
|
636
|
+
# No official return value, but return true so that in interactive
|
637
|
+
# use the method hints that it succeeded.
|
638
|
+
true
|
639
|
+
end
|
640
|
+
|
641
|
+
# @api private
|
642
|
+
def starting_transaction?
|
643
|
+
within_states?(STARTING_TRANSACTION_STATE)
|
644
|
+
end
|
645
|
+
|
646
|
+
# Whether or not the session is currently in a transaction.
|
647
|
+
#
|
648
|
+
# @example Is the session in a transaction?
|
649
|
+
# session.in_transaction?
|
650
|
+
#
|
651
|
+
# @return [ true | false ] Whether or not the session in a transaction.
|
652
|
+
#
|
653
|
+
# @since 2.6.0
|
654
|
+
def in_transaction?
|
655
|
+
within_states?(STARTING_TRANSACTION_STATE, TRANSACTION_IN_PROGRESS_STATE)
|
656
|
+
end
|
657
|
+
|
658
|
+
# @return [ true | false ] Whether the session is currently committing a
|
659
|
+
# transaction.
|
660
|
+
#
|
661
|
+
# @api private
|
662
|
+
def committing_transaction?
|
663
|
+
!!@committing_transaction
|
664
|
+
end
|
665
|
+
|
666
|
+
# Pins this session to the specified server, which should be a mongos.
|
667
|
+
#
|
668
|
+
# @param [ Server ] server The server to pin this session to.
|
669
|
+
#
|
670
|
+
# @api private
|
671
|
+
def pin(server)
|
672
|
+
if server.nil?
|
673
|
+
raise ArgumentError, 'Cannot pin to a nil server'
|
674
|
+
end
|
675
|
+
if Lint.enabled?
|
676
|
+
unless server.mongos?
|
677
|
+
raise Error::LintError, "Attempted to pin the session to server #{server.summary} which is not a mongos"
|
678
|
+
end
|
679
|
+
end
|
680
|
+
@pinned_server = server
|
681
|
+
end
|
682
|
+
|
683
|
+
# Unpins this session from the pinned server, if the session was pinned.
|
684
|
+
#
|
685
|
+
# @api private
|
686
|
+
def unpin
|
687
|
+
@pinned_server = nil
|
688
|
+
end
|
689
|
+
|
690
|
+
# Unpins this session from the pinned server, if the session was pinned
|
691
|
+
# and the specified exception instance and the session's transaction state
|
692
|
+
# require it to be unpinned.
|
693
|
+
#
|
694
|
+
# The exception instance should already have all of the labels set on it
|
695
|
+
# (both client- and server-side generated ones).
|
696
|
+
#
|
697
|
+
# @param [ Error ] The exception instance to process.
|
698
|
+
#
|
699
|
+
# @api private
|
700
|
+
def unpin_maybe(error)
|
701
|
+
if !within_states?(Session::NO_TRANSACTION_STATE) &&
|
702
|
+
error.label?('TransientTransactionError')
|
703
|
+
then
|
704
|
+
unpin
|
705
|
+
end
|
706
|
+
|
707
|
+
if committing_transaction? &&
|
708
|
+
error.label?('UnknownTransactionCommitResult')
|
709
|
+
then
|
710
|
+
unpin
|
711
|
+
end
|
712
|
+
end
|
713
|
+
|
714
|
+
# Add the autocommit field to a command document if applicable.
|
715
|
+
#
|
716
|
+
# @example
|
717
|
+
# session.add_autocommit!(cmd)
|
718
|
+
#
|
719
|
+
# @return [ Hash, BSON::Document ] The command document.
|
720
|
+
#
|
721
|
+
# @since 2.6.0
|
722
|
+
# @api private
|
723
|
+
def add_autocommit!(command)
|
724
|
+
command.tap do |c|
|
725
|
+
c[:autocommit] = false if in_transaction?
|
726
|
+
end
|
727
|
+
end
|
728
|
+
|
729
|
+
# Add this session's id to a command document.
|
730
|
+
#
|
731
|
+
# @example
|
732
|
+
# session.add_id!(cmd)
|
733
|
+
#
|
734
|
+
# @return [ Hash, BSON::Document ] The command document.
|
735
|
+
#
|
736
|
+
# @since 2.5.0
|
737
|
+
# @api private
|
738
|
+
def add_id!(command)
|
739
|
+
command.merge!(lsid: session_id)
|
740
|
+
end
|
741
|
+
|
742
|
+
# Add the startTransaction field to a command document if applicable.
|
743
|
+
#
|
744
|
+
# @example
|
745
|
+
# session.add_start_transaction!(cmd)
|
746
|
+
#
|
747
|
+
# @return [ Hash, BSON::Document ] The command document.
|
748
|
+
#
|
749
|
+
# @since 2.6.0
|
750
|
+
# @api private
|
751
|
+
def add_start_transaction!(command)
|
752
|
+
command.tap do |c|
|
753
|
+
if starting_transaction?
|
754
|
+
c[:startTransaction] = true
|
755
|
+
end
|
756
|
+
end
|
757
|
+
end
|
758
|
+
|
759
|
+
# Add the transaction number to a command document if applicable.
|
760
|
+
#
|
761
|
+
# @example
|
762
|
+
# session.add_txn_num!(cmd)
|
763
|
+
#
|
764
|
+
# @return [ Hash, BSON::Document ] The command document.
|
765
|
+
#
|
766
|
+
# @since 2.6.0
|
767
|
+
# @api private
|
768
|
+
def add_txn_num!(command)
|
769
|
+
command.tap do |c|
|
770
|
+
c[:txnNumber] = BSON::Int64.new(@server_session.txn_num) if in_transaction?
|
771
|
+
end
|
772
|
+
end
|
773
|
+
|
774
|
+
# Add the transactions options if applicable.
|
775
|
+
#
|
776
|
+
# @example
|
777
|
+
# session.add_txn_opts!(cmd)
|
778
|
+
#
|
779
|
+
# @return [ Hash, BSON::Document ] The command document.
|
780
|
+
#
|
781
|
+
# @since 2.6.0
|
782
|
+
# @api private
|
783
|
+
def add_txn_opts!(command, read)
|
784
|
+
command.tap do |c|
|
785
|
+
# The read concern should be added to any command that starts a transaction.
|
786
|
+
if starting_transaction?
|
787
|
+
# https://jira.mongodb.org/browse/SPEC-1161: transaction's
|
788
|
+
# read concern overrides collection/database/client read concerns,
|
789
|
+
# even if transaction's read concern is not set.
|
790
|
+
# Read concern here is the one sent to the server and may
|
791
|
+
# include afterClusterTime.
|
792
|
+
if rc = c[:readConcern]
|
793
|
+
rc = rc.dup
|
794
|
+
rc.delete(:level)
|
795
|
+
end
|
796
|
+
if txn_read_concern
|
797
|
+
if rc
|
798
|
+
rc.update(txn_read_concern)
|
799
|
+
else
|
800
|
+
rc = txn_read_concern.dup
|
801
|
+
end
|
802
|
+
end
|
803
|
+
if rc.nil? || rc.empty?
|
804
|
+
c.delete(:readConcern)
|
805
|
+
else
|
806
|
+
c[:readConcern ] = Options::Mapper.transform_values_to_strings(rc)
|
807
|
+
end
|
808
|
+
end
|
809
|
+
|
810
|
+
# We need to send the read concern level as a string rather than a symbol.
|
811
|
+
if c[:readConcern]
|
812
|
+
c[:readConcern] = Options::Mapper.transform_values_to_strings(c[:readConcern])
|
813
|
+
end
|
814
|
+
|
815
|
+
if c[:commitTransaction]
|
816
|
+
if max_time_ms = txn_options[:max_commit_time_ms]
|
817
|
+
c[:maxTimeMS] = max_time_ms
|
818
|
+
end
|
819
|
+
end
|
820
|
+
|
821
|
+
# The write concern should be added to any abortTransaction or commitTransaction command.
|
822
|
+
if (c[:abortTransaction] || c[:commitTransaction])
|
823
|
+
if @already_committed
|
824
|
+
wc = BSON::Document.new(c[:writeConcern] || txn_write_concern || {})
|
825
|
+
wc.merge!(w: :majority)
|
826
|
+
wc[:wtimeout] ||= 10000
|
827
|
+
c[:writeConcern] = wc
|
828
|
+
elsif txn_write_concern
|
829
|
+
c[:writeConcern] ||= txn_write_concern
|
830
|
+
end
|
831
|
+
end
|
832
|
+
|
833
|
+
# A non-numeric write concern w value needs to be sent as a string rather than a symbol.
|
834
|
+
if c[:writeConcern] && c[:writeConcern][:w] && c[:writeConcern][:w].is_a?(Symbol)
|
835
|
+
c[:writeConcern][:w] = c[:writeConcern][:w].to_s
|
836
|
+
end
|
837
|
+
end
|
724
838
|
end
|
725
839
|
|
726
|
-
#
|
840
|
+
# Remove the read concern and/or write concern from the command if not applicable.
|
727
841
|
#
|
728
|
-
# @example
|
729
|
-
# session.
|
842
|
+
# @example
|
843
|
+
# session.suppress_read_write_concern!(cmd)
|
730
844
|
#
|
731
|
-
# @return [
|
845
|
+
# @return [ Hash, BSON::Document ] The command document.
|
732
846
|
#
|
733
847
|
# @since 2.6.0
|
734
|
-
|
735
|
-
|
848
|
+
# @api private
|
849
|
+
def suppress_read_write_concern!(command)
|
850
|
+
command.tap do |c|
|
851
|
+
next unless in_transaction?
|
852
|
+
|
853
|
+
c.delete(:readConcern) unless starting_transaction?
|
854
|
+
c.delete(:writeConcern) unless c[:commitTransaction] || c[:abortTransaction]
|
855
|
+
end
|
736
856
|
end
|
737
857
|
|
738
|
-
#
|
858
|
+
# Ensure that the read preference of a command primary.
|
739
859
|
#
|
740
|
-
#
|
860
|
+
# @example
|
861
|
+
# session.validate_read_preference!(command)
|
741
862
|
#
|
742
|
-
#
|
743
|
-
#
|
744
|
-
# should be prepared to be called more than once. The driver may retry
|
745
|
-
# the commit command within an active transaction or it may repeat the
|
746
|
-
# transaction and invoke the block again, depending on the error
|
747
|
-
# encountered if any. Note also that the retries may be executed against
|
748
|
-
# different servers.
|
863
|
+
# @raise [ Mongo::Error::InvalidTransactionOperation ] If the read preference of the command is
|
864
|
+
# not primary.
|
749
865
|
#
|
750
|
-
#
|
751
|
-
#
|
866
|
+
# @since 2.6.0
|
867
|
+
# @api private
|
868
|
+
def validate_read_preference!(command)
|
869
|
+
return unless in_transaction?
|
870
|
+
return unless command['$readPreference']
|
871
|
+
|
872
|
+
mode = command['$readPreference']['mode'] || command['$readPreference'][:mode]
|
873
|
+
|
874
|
+
if mode && mode != 'primary'
|
875
|
+
raise Mongo::Error::InvalidTransactionOperation.new(
|
876
|
+
"read preference in a transaction must be primary (requested: #{mode})"
|
877
|
+
)
|
878
|
+
end
|
879
|
+
end
|
880
|
+
|
881
|
+
# Update the state of the session due to a (non-commit and non-abort) operation being run.
|
752
882
|
#
|
753
|
-
#
|
754
|
-
#
|
755
|
-
|
756
|
-
|
883
|
+
# @since 2.6.0
|
884
|
+
# @api private
|
885
|
+
def update_state!
|
886
|
+
case @state
|
887
|
+
when STARTING_TRANSACTION_STATE
|
888
|
+
@state = TRANSACTION_IN_PROGRESS_STATE
|
889
|
+
when TRANSACTION_COMMITTED_STATE, TRANSACTION_ABORTED_STATE
|
890
|
+
@state = NO_TRANSACTION_STATE
|
891
|
+
end
|
892
|
+
end
|
893
|
+
|
894
|
+
# Validate the session.
|
757
895
|
#
|
758
|
-
#
|
759
|
-
#
|
760
|
-
# executing. This timeout is not configurable and may change in a future
|
761
|
-
# driver version.
|
896
|
+
# @example
|
897
|
+
# session.validate!(cluster)
|
762
898
|
#
|
763
|
-
# @
|
764
|
-
# itself is placed in a loop, its block should not call next or break to
|
765
|
-
# control the outer loop because this will instead affect the loop in
|
766
|
-
# with_transaction. The driver will warn and abort the transaction
|
767
|
-
# if it detects this situation.
|
899
|
+
# @param [ Cluster ] cluster The cluster the session is attempted to be used with.
|
768
900
|
#
|
769
|
-
# @
|
770
|
-
# session.with_transaction(write_concern: {w: :majority}) do
|
771
|
-
# collection.update_one({ id: 3 }, { '$set' => { status: 'Inactive'} },
|
772
|
-
# session: session)
|
901
|
+
# @return [ nil ] nil if the session is valid.
|
773
902
|
#
|
774
|
-
#
|
903
|
+
# @raise [ Mongo::Error::InvalidSession ] Raise error if the session is not valid.
|
775
904
|
#
|
776
|
-
# @
|
777
|
-
#
|
778
|
-
|
779
|
-
|
780
|
-
|
905
|
+
# @since 2.5.0
|
906
|
+
# @api private
|
907
|
+
def validate!(cluster)
|
908
|
+
check_matching_cluster!(cluster)
|
909
|
+
check_if_ended!
|
910
|
+
self
|
911
|
+
end
|
912
|
+
|
913
|
+
# Process a response from the server that used this session.
|
781
914
|
#
|
782
|
-
#
|
783
|
-
#
|
915
|
+
# @example Process a response from the server.
|
916
|
+
# session.process(result)
|
784
917
|
#
|
785
|
-
# @param [
|
786
|
-
# These are the same options that start_transaction accepts.
|
918
|
+
# @param [ Operation::Result ] result The result from the operation.
|
787
919
|
#
|
788
|
-
# @
|
789
|
-
# progress or if the write concern is unacknowledged.
|
920
|
+
# @return [ Operation::Result ] The result.
|
790
921
|
#
|
791
|
-
# @since 2.
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
if options
|
799
|
-
commit_options[:write_concern] = options[:write_concern]
|
922
|
+
# @since 2.5.0
|
923
|
+
# @api private
|
924
|
+
def process(result)
|
925
|
+
unless implicit?
|
926
|
+
set_operation_time(result)
|
927
|
+
if cluster_time_doc = result.cluster_time
|
928
|
+
advance_cluster_time(cluster_time_doc)
|
800
929
|
end
|
801
|
-
|
802
|
-
|
803
|
-
begin
|
804
|
-
rv = yield self
|
805
|
-
rescue Exception => e
|
806
|
-
if within_states?(STARTING_TRANSACTION_STATE, TRANSACTION_IN_PROGRESS_STATE)
|
807
|
-
abort_transaction
|
808
|
-
transaction_in_progress = false
|
809
|
-
end
|
810
|
-
|
811
|
-
if Time.now >= deadline
|
812
|
-
transaction_in_progress = false
|
813
|
-
raise
|
814
|
-
end
|
815
|
-
|
816
|
-
if e.is_a?(Mongo::Error) && e.label?(Mongo::Error::TRANSIENT_TRANSACTION_ERROR_LABEL)
|
817
|
-
next
|
818
|
-
end
|
819
|
-
|
820
|
-
raise
|
821
|
-
else
|
822
|
-
if within_states?(TRANSACTION_ABORTED_STATE, NO_TRANSACTION_STATE, TRANSACTION_COMMITTED_STATE)
|
823
|
-
transaction_in_progress = false
|
824
|
-
return rv
|
825
|
-
end
|
930
|
+
end
|
931
|
+
@server_session.set_last_use!
|
826
932
|
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
return rv
|
831
|
-
rescue Mongo::Error => e
|
832
|
-
if e.label?(Mongo::Error::UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)
|
833
|
-
# WriteConcernFailed
|
834
|
-
if e.is_a?(Mongo::Error::OperationFailure) && e.code == 64 && e.wtimeout?
|
835
|
-
transaction_in_progress = false
|
836
|
-
raise
|
837
|
-
end
|
838
|
-
if Time.now >= deadline
|
839
|
-
transaction_in_progress = false
|
840
|
-
raise
|
841
|
-
end
|
842
|
-
wc_options = case v = commit_options[:write_concern]
|
843
|
-
when WriteConcern::Base
|
844
|
-
v.options
|
845
|
-
when nil
|
846
|
-
{}
|
847
|
-
else
|
848
|
-
v
|
849
|
-
end
|
850
|
-
commit_options[:write_concern] = wc_options.merge(w: :majority)
|
851
|
-
retry
|
852
|
-
elsif e.label?(Mongo::Error::TRANSIENT_TRANSACTION_ERROR_LABEL)
|
853
|
-
if Time.now >= deadline
|
854
|
-
transaction_in_progress = false
|
855
|
-
raise
|
856
|
-
end
|
857
|
-
next
|
858
|
-
else
|
859
|
-
transaction_in_progress = false
|
860
|
-
raise
|
861
|
-
end
|
862
|
-
end
|
933
|
+
if doc = result.reply && result.reply.documents.first
|
934
|
+
if doc[:recoveryToken]
|
935
|
+
self.recovery_token = doc[:recoveryToken]
|
863
936
|
end
|
864
937
|
end
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
938
|
+
|
939
|
+
result
|
940
|
+
end
|
941
|
+
|
942
|
+
# Advance the cached operation time for this session.
|
943
|
+
#
|
944
|
+
# @example Advance the operation time.
|
945
|
+
# session.advance_operation_time(timestamp)
|
946
|
+
#
|
947
|
+
# @param [ BSON::Timestamp ] new_operation_time The new operation time.
|
948
|
+
#
|
949
|
+
# @return [ BSON::Timestamp ] The max operation time, considering the current and new times.
|
950
|
+
#
|
951
|
+
# @since 2.5.0
|
952
|
+
def advance_operation_time(new_operation_time)
|
953
|
+
if @operation_time
|
954
|
+
@operation_time = [ @operation_time, new_operation_time ].max
|
955
|
+
else
|
956
|
+
@operation_time = new_operation_time
|
872
957
|
end
|
873
958
|
end
|
874
959
|
|
875
|
-
#
|
876
|
-
# active transaction.
|
960
|
+
# Increment and return the next transaction number.
|
877
961
|
#
|
878
|
-
#
|
962
|
+
# @example Get the next transaction number.
|
963
|
+
# session.next_txn_num
|
879
964
|
#
|
880
|
-
# @
|
881
|
-
# session.txn_read_preference
|
965
|
+
# @return [ Integer ] The next transaction number.
|
882
966
|
#
|
883
|
-
# @
|
967
|
+
# @since 2.5.0
|
968
|
+
# @api private
|
969
|
+
def next_txn_num
|
970
|
+
if ended?
|
971
|
+
raise Error::SessionEnded
|
972
|
+
end
|
973
|
+
|
974
|
+
@server_session.next_txn_num
|
975
|
+
end
|
976
|
+
|
977
|
+
# Get the current transaction number.
|
978
|
+
#
|
979
|
+
# @example Get the current transaction number.
|
980
|
+
# session.txn_num
|
981
|
+
#
|
982
|
+
# @return [ Integer ] The current transaction number.
|
884
983
|
#
|
885
984
|
# @since 2.6.0
|
886
|
-
def
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
rp
|
891
|
-
end
|
985
|
+
def txn_num
|
986
|
+
if ended?
|
987
|
+
raise Error::SessionEnded
|
988
|
+
end
|
892
989
|
|
893
|
-
|
894
|
-
@client.cluster
|
990
|
+
@server_session.txn_num
|
895
991
|
end
|
896
992
|
|
897
|
-
|
993
|
+
private
|
898
994
|
|
899
995
|
# Get the read concern the session will use when starting a transaction.
|
900
996
|
#
|
@@ -908,19 +1004,13 @@ module Mongo
|
|
908
1004
|
# @since 2.9.0
|
909
1005
|
def txn_read_concern
|
910
1006
|
# Read concern is inherited from client but not db or collection.
|
911
|
-
txn_options
|
1007
|
+
txn_options[:read_concern] || @client.read_concern
|
912
1008
|
end
|
913
1009
|
|
914
|
-
private
|
915
|
-
|
916
1010
|
def within_states?(*states)
|
917
1011
|
states.include?(@state)
|
918
1012
|
end
|
919
1013
|
|
920
|
-
def starting_transaction?
|
921
|
-
within_states?(STARTING_TRANSACTION_STATE)
|
922
|
-
end
|
923
|
-
|
924
1014
|
def check_if_no_transaction!
|
925
1015
|
return unless within_states?(NO_TRANSACTION_STATE)
|
926
1016
|
|
@@ -929,17 +1019,10 @@ module Mongo
|
|
929
1019
|
end
|
930
1020
|
|
931
1021
|
def txn_write_concern
|
932
|
-
|
1022
|
+
txn_options[:write_concern] ||
|
933
1023
|
(@client.write_concern && @client.write_concern.options)
|
934
1024
|
end
|
935
1025
|
|
936
|
-
def non_primary_read_preference_mode?(command)
|
937
|
-
return false unless command['$readPreference']
|
938
|
-
|
939
|
-
mode = command['$readPreference']['mode'] || command['$readPreference'][:mode]
|
940
|
-
mode && mode != 'primary'
|
941
|
-
end
|
942
|
-
|
943
1026
|
# Returns causal consistency document if the last operation time is
|
944
1027
|
# known and causal consistency is enabled, otherwise returns nil.
|
945
1028
|
def causal_consistency_doc
|
@@ -964,16 +1047,6 @@ module Mongo
|
|
964
1047
|
end
|
965
1048
|
end
|
966
1049
|
|
967
|
-
def set_cluster_time(result)
|
968
|
-
if cluster_time_doc = result.cluster_time
|
969
|
-
if @cluster_time.nil?
|
970
|
-
@cluster_time = cluster_time_doc
|
971
|
-
elsif cluster_time_doc[Cluster::CLUSTER_TIME] > @cluster_time[Cluster::CLUSTER_TIME]
|
972
|
-
@cluster_time = cluster_time_doc
|
973
|
-
end
|
974
|
-
end
|
975
|
-
end
|
976
|
-
|
977
1050
|
def check_if_ended!
|
978
1051
|
raise Mongo::Error::InvalidSession.new(SESSION_ENDED_ERROR_MSG) if ended?
|
979
1052
|
end
|